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/.env.development.sample b/.env.development.sample new file mode 100644 index 00000000..03af4e23 --- /dev/null +++ b/.env.development.sample @@ -0,0 +1,28 @@ +NAME=database +ENVIRONMENT=development + +HOST=local.solarbuildingenvelopes.com +SUBDOMAIN=www +HTTP_PORT=8500 +HTTPS_PORT=7501 + +METABASE_HOST=www.local.buildingenvelopedata.org +METABASE_HTTPS_PORT=7001 + +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DATABASE_NAME=xbase + +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 + +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4517 + +VERIFICATION_CODE=secret + +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase diff --git a/.env.production.sample b/.env.production.sample index 8ba75e77..b46b26cd 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,15 +1,27 @@ NAME=database_production +ENVIRONMENT=production +TARGET= -HOST=www.solarbuildingenvelopes.com +HOST=solarbuildingenvelopes.com +SUBDOMAIN=www HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org -ACCESS_TOKEN= +POSTGRES_USER=postgres +POSTGRES_PASSWORD= +POSTGRES_DATABASE_NAME=xbase + +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= -SMTP_HOST=mail-mtaKA26.fraunhofer.de -SMTP_PORT=25 -RELAY_SMTP_HOST= -RELAY_SMTP_PORT=25 -RELAY_ALLOWED_EMAILS= +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4317 + +VERIFICATION_CODE= + +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.env.sample b/.env.sample deleted file mode 100644 index c94f4e4f..00000000 --- a/.env.sample +++ /dev/null @@ -1,22 +0,0 @@ -NAME=database - -HOST=local.solarbuildingenvelopes.com -HTTP_PORT=5050 -HTTPS_PORT=5051 - -METABASE_HOST=local.buildingenvelopedata.org:4041 - -ACCESS_TOKEN=secret -OPEN_ID_CONNECT_CLIENT_SECRET=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 - -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 - -SMTP_HOST=email -SMTP_PORT=25 diff --git a/.env.staging.sample b/.env.staging.sample index 85145cab..6d2f6658 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -1,15 +1,27 @@ NAME=database_staging +ENVIRONMENT=staging +TARGET= -HOST=staging.solarbuildingenvelopes.com +HOST=solarbuildingenvelopes.com +SUBDOMAIN=staging HTTP_PORT=9000 METABASE_HOST=staging.buildingenvelopedata.org -ACCESS_TOKEN= +POSTGRES_USER=postgres +POSTGRES_PASSWORD= +POSTGRES_DATABASE_NAME=xbase + +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= -SMTP_HOST=email -SMTP_PORT=25 -RELAY_SMTP_HOST=mail-mtaKA26.fraunhofer.de -RELAY_SMTP_PORT=25 -RELAY_ALLOWED_EMAILS= +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4317 + +VERIFICATION_CODE= + +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..7860975d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.large.* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yaml similarity index 94% rename from .github/workflows/dotnet.yml rename to .github/workflows/dotnet.yaml index 1a3d9911..7ff9ef0a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yaml @@ -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 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.yaml b/.github/workflows/label-issues.yaml new file mode 100644 index 00000000..254a712a --- /dev/null +++ b/.github/workflows/label-issues.yaml @@ -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"] + }) 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 4d038597..a4ad2604 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,12 @@ +/Makefile +/docker-compose.yaml +*/Dockerfile /examples/*.json -/secrets -/ssl /machine/*.retry /nginx/html/maintenance.html -/commit -/backup - -.env -.vscode +/tags* +/.deploy-history +/.env # Compiled output dist @@ -25,6 +24,9 @@ coverage # Backups *.bak +/backup +/backups +/.content-addressable-storage # Image Diagrams /diagrams/*.png diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 00000000..75ba64b6 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,4 @@ +ignored: + - DL3008 + +strict-labels: true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e69de29b 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/.sqlfluff b/.sqlfluff new file mode 100644 index 00000000..e69de29b diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1d5730..1993b3b0 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 - @@ -121,6 +119,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - -[Unreleased]: https://github.com/building-envelope-data/database/compare/v1.0.0...HEAD +## [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 -[v1.0.0]: https://github.com/building-envelope-data/database/compare/d45f78d150b58e016d63a827124b740765c4d350...v1.0.0 +[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/Dockerfile-show-build-context b/Dockerfile-show-build-context deleted file mode 100644 index 3348ecb9..00000000 --- a/Dockerfile-show-build-context +++ /dev/null @@ -1,5 +0,0 @@ -FROM busybox - -RUN mkdir /tmp/build/ -COPY . /tmp/build/ -RUN find /tmp/build/ diff --git a/Dockerfile.build-context b/Dockerfile.build-context new file mode 100644 index 00000000..85baee60 --- /dev/null +++ b/Dockerfile.build-context @@ -0,0 +1,9 @@ +# syntax=docker/dockerfile:1.21 +# check=error=true +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + +FROM busybox:1.37 + +RUN mkdir /tmp/build/ +COPY . /tmp/build/ +RUN find /tmp/build/ diff --git a/Makefile b/Makefile deleted file mode 100644 index c68ba61c..00000000 --- a/Makefile +++ /dev/null @@ -1,396 +0,0 @@ -# Concise introduction to GNU Make: -# https://swcarpentry.github.io/make-novice/reference.html - -include .env - -docker_compose = \ - docker-compose \ - --file docker-compose.yml \ - --project-name ${NAME} - -# 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 - -# ----------------------------- # -# 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) -.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 - -remove : ## Remove stopped containers - ${docker_compose} rm -.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 \ - --detach -.PHONY : up - -down : ## Stop containers and remove containers, networks, volumes, and images created by `up` - ${docker_compose} down \ - --remove-orphans -.PHONY : down - -restart : ## Restart all stopped and running containers - ${docker_compose} restart -.PHONY : restart - -logs : ## Follow logs - ${docker_compose} logs \ - --follow -.PHONY : logs - -exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CONTAINER}` container (after starting all containers if necessary) - ${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 an existing `frontend` container (after starting all containers if necessary) -.PHONY : execf - -execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against an existing `backend` container (after starting all containers if necessary) -.PHONY : execb - -run : up ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container (after starting all containers if necessary) - ${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 (after starting all containers if necessary) -.PHONY : runf - -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 : execf ## Enter shell in an existing `frontend` container (after starting all containers if necessary) -.PHONY : shellf - -shellb : COMMAND = bash -shellb : runb ## Enter shell in a fresh `backend` container (after starting all containers if necessary) -.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) -.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} exec \ - --privileged \ - backend \ - bash -c " \ - make trace \ - " -.PHONY : traceb - -shelln : up ## Enter shell in an existing `nginx` container (after starting all containers if necessary) - ${docker_compose} exec \ - nginx \ - bash -.PHONY : shelld - -psql : ## Enter PostgreSQL interactive terminal in the running `database` container - ${docker_compose} exec \ - database \ - psql \ - --username postgres \ - --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 -.PHONY : shelld - -list : ## List all containers with health status - ${docker_compose} ps \ - --all -.PHONY : list - -createdb : ## Create databases - ${docker_compose} exec \ - database \ - bash -c " \ - createdb --username postgres xbase_development ; \ - " -.PHONY : createdb - -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 - -prepare-release : ## Prepare release - ${docker_compose} run \ - --user $(shell id --user):$(shell id --group) \ - backend \ - make prepare-release -.PHONY : prepare-release - -# --------------------- # -# Generate 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 -generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate - mkdir --parents ./ssl/ - docker run \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.21.1 \ - 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 \ - -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 \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.21.1 \ - 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 \ - -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 diff --git a/Makefile.production b/Makefile.production deleted file mode 100644 index 0f53b539..00000000 --- a/Makefile.production +++ /dev/null @@ -1,297 +0,0 @@ -# Concise introduction to GNU Make: -# https://swcarpentry.github.io/make-novice/reference.html - -include .env - -docker_compose = \ - docker-compose \ - --file docker-compose.production.yml \ - --project-name ${NAME} - -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 - -name : ## Print value of variable `${NAME}` - @echo ${NAME} -.PHONY : name - -build : ## Build images - ${docker_compose} build -.PHONY : build - -up : ## (Re)create and start containers - ${docker_compose} up \ - --remove-orphans \ - --detach -.PHONY : up - -down : ## Stop containers and remove containers, networks, volumes, and images created by `up` - ${docker_compose} down \ - --remove-orphans -.PHONY : down - -restart : ## Restart all stopped and running containers - ${docker_compose} restart -.PHONY : restart - -logs : ## Follow logs - ${docker_compose} logs \ - --follow -.PHONY : logs - -exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CONTAINER}` container (after starting all containers if necessary) - ${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) - -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 - -createdb : ## Create database - ${docker_compose} run \ - --name create_${NAME}_database \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} create_${NAME}_database) != "healthy" ]; do sleep 1; done - docker exec \ - create_${NAME}_database \ - createdb \ - --username postgres \ - xbase - docker container stop create_${NAME}_database - docker container rm --volumes create_${NAME}_database -.PHONY : createdb - -psql : ## Enter PostgreSQL interactive terminal in the running `database` service - ${docker_compose} exec \ - database \ - psql \ - --username=postgres \ - --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 - 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 - -# 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 -# 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` - mkdir --parents ${BACKUP_DIRECTORY} - ${docker_compose} run \ - --name backup_${NAME}_database \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} backup_${NAME}_database) != "healthy" ]; do sleep 1; done - docker exec \ - backup_${NAME}_database \ - 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_compose} run \ - --name backup_${NAME}_files \ - --detach \ - backend - docker run \ - --rm \ - --volumes-from backup_${NAME}_files \ - --volume ${BACKUP_DIRECTORY}:/backup \ - debian:buster-slim \ - tar \ - --verbose \ - --create \ - --gzip \ - --file=/backup/${files_archive_name} \ - --directory=/app \ - ./files - docker container stop backup_${NAME}_files - docker container rm --volumes backup_${NAME}_files -.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 - 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 - -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 \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} restore_${NAME}_database) != "healthy" ]; do sleep 1; done - gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ - | docker exec \ - --interactive \ - restore_${NAME}_database \ - psql \ - --echo-all \ - --file=- \ - --username=postgres \ - --dbname=postgres - docker container stop restore_${NAME}_database - docker container rm --volumes restore_${NAME}_database - ${docker_compose} run \ - --name backup_${NAME}_files \ - --detach \ - backend - docker run \ - --rm \ - --volumes-from backup_${NAME}_files \ - --volume ${BACKUP_DIRECTORY}:/backup \ - debian:buster-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 backup_${NAME}_files - docker container rm --volumes backup_${NAME}_files -.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 - -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 - $(shell 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 - -migrate : FROM = $(file < ./backend/src/Migrations/previous) -migrate : TO = $(file < ./backend/src/Migrations/current) -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_compose} run \ - --name migrate_${NAME}_database \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} migrate_${NAME}_database) != "healthy" ]; do sleep 1; done - cat ./backend/src/Migrations/migrate_from_${FROM}_to_${TO}.sql \ - | docker exec \ - --interactive \ - migrate_${NAME}_database \ - psql \ - --echo-all \ - --file=- \ - --username=postgres \ - --dbname=xbase - docker container stop migrate_${NAME}_database - docker container rm --volumes migrate_${NAME}_database -.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 : ## Deploy services - ${docker_compose} pull - ${docker_compose} build \ - --pull - ${docker_compose} up \ - --force-recreate \ - --renew-anon-volumes \ - --remove-orphans \ - --detach -.PHONY : deploy-services - -run-tests : COMMAND = true -run-tests : execb ## Run tests -.PHONY : run-tests diff --git a/README.md b/README.md index da7feed8..ce65a2da 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,549 @@ # 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). - -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). - -## Getting started - -### On your Linux machine - -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. 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. 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. 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/). +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). +When doing so, please adhere to our +[Code of Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). + +## Contents + +[Development](#development) + +- [Getting Started](#getting-started) +- [Migrating the Database](#migrating-the-database) +- [Developing with Visual Studio Code](#developing-with-visual-studio-code) +- [Troubleshooting](#troubleshooting-0) + +[Deployment](#deployment) + +- [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) + +## Development + +### Getting Started + +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. Change to the parent directory of `./machine` by running `cd ..`. + +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 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. 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`. -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/`. + +1. Generate a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in `./.env` by running + `make manage COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` + with your information filled in, for example, + `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`. + +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://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 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}`, + + 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 `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`. + `./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 + [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`. + +### Migrating the Database + +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 +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. + +### 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 `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. + 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 `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 + [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.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. + +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 `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/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 +example, open some source files to set breakpoints, navigate through 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). + +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.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). +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/net10.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 && ./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 +`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 +directory `./frontend/__generated__`, then **temporarily** ignore TypeScript +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: + +``` +typescript: { + ignoreBuildErrors: true, +}, +``` -The same works for frontend containers by running `make shellf`. +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 -### Setting up a Debian production machine +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 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 + `environment=production`. + 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`. - 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. 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: 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` + - 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 Façades. + + 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 /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 + 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`; + - `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 + for HTTPS requests (see `PRODUCTION_HTTP_PORT` and + `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 + 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_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_FINGERPRINT` and + `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase + 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 a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file + by running + `make manage COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` + with your information filled in, for example, + `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. Switch to the Git branch, tag, or commit you want to deploy by either running + `git switch ${BRANCH}`, + `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`. + + 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 -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. 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. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yml) +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.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), + 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 switch to it by running + `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`. +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 - `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 +1. Fetch the release branch by running `git fetch` and switch to it by running + `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 + server's IP address. 1. Enter a shell on the production machine using `ssh`. -1. Change to the staging envrionment by running `cd /app/staging`. +1. Navigate into `/app/production` by running `cd /app/production`. +1. Back up the production database by running + `./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 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 - `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`, - figure out what went wrong, apply the necessary fixes to the codebase, - create a new release, and try to deploy that release instead. + `./deploy.mk do TARGET=${TAG}`, where `${TAG}` is + the release tag to be deployed, for example, `v1.0.0`. +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 ./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 + 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 the `.env` file 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, 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. -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. +### Troubleshooting + +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 `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 +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 + +``` +./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 +reset the transaction log by entering the database container with + +``` +make shell SERVICE=database +``` + +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. + +#### Update a SQL field manually -## Useful Resources +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. -- [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/) +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 + `./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 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 + 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/backend/.dockerignore b/backend/.dockerignore index f12b3ac4..591bb290 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -1,366 +1,18 @@ # https://docs.docker.com/engine/reference/builder/#dockerignore-file - -**/.* -**/*.md -**/*.bak -Dockerfile* - -**/core -**/TestResult*/ - -### 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 +# The inverse dockerignore file: .dockerinclude + +* + +!. +!**/*.cs +!**/*.cshtml +!**/*.csproj +!*.sln +!*.props +!**/*.graphql +!**/*.json + +src/bin/** +src/obj/** +src/bin\\Debug/** +src/obj\\Debug/** diff --git a/.editorconfig b/backend/.editorconfig similarity index 97% rename from .editorconfig rename to backend/.editorconfig index e3a4b5a2..5dcf14c1 100644 --- a/.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}] @@ -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 @@ -206,6 +209,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 diff --git a/backend/.gitignore b/backend/.gitignore index 29292c49..decf6a4f 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,14 +1,15 @@ +/Dockerfile /src/core +/src/bin\\Debug /src/tempkey.rsa /src/wwwroot/assets/ /src/files/ +/src/gpg-keys/ /trace.* /.secrets +/.mono -/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/.vscode/settings.json b/backend/.vscode/settings.json new file mode 100644 index 00000000..5c1847af --- /dev/null +++ b/backend/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnSave": true, + "[csharp]": { + "editor.defaultFormatter": "ms-dotnettools.csharp" + }, + "workbench.editor.enablePreview": false, + "workbench.editor.enablePreviewFromQuickOpen": false, + "dotnet.backgroundAnalysis.compilerDiagnosticsScope": "fullSolution" +} \ No newline at end of file diff --git a/backend/Directory.Build.props b/backend/Directory.Build.props new file mode 100644 index 00000000..1de6f3ca --- /dev/null +++ b/backend/Directory.Build.props @@ -0,0 +1,20 @@ + + + + net10.0 + 14.0 + enable + + + + + true + true + true + latest-Recommended + true + + true + + + diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 65a0cea4..00000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,293 +0,0 @@ -# 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 - -ARG GROUP_ID -ARG USER_ID -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) -RUN \ - addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me - -############# -# As `root` # -############# - -# `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 -# https://docs.microsoft.com/en-us/dotnet/core/diagnostics/logging-tracing#logging-events -# and -# https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md#collecting-a-trace -# and -# https://stackoverflow.com/questions/55477684/how-to-configure-network-tracing-dotnet-core-for-httpclient-calls/58700657#58700657 -# and -# 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 \ - --yes \ - --no-install-recommends \ - curl \ - dumb-init \ - gawk \ - lldb \ - make \ - # npm \ - openssl && \ - 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 \ - --yes \ - --no-install-recommends \ - bash \ - jq \ - neovim && \ - rm \ - --recursive \ - --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 - -ENV HOME=/home/me -RUN \ - mkdir --parents ${HOME}/app && \ - chown \ - me:us \ - ${HOME}/app && \ - ln --symbolic ${HOME}/app /app - -########### -# As `me` # -########### -USER me -WORKDIR /app - -# Install the tools specified in the manifest file `dotnet-tools.json`. -# For a comprehensive list of tools see https://github.com/natemcmaster/dotnet-tools -# Diagnostics is provided by `counters`, `dump`, and `trace`, see https://devblogs.microsoft.com/dotnet/introducing-diagnostics-improvements-in-net-core-3-0/ -# Code formatting is provided by `format` -# Code generation is provided by `aspnet-codegenerator` -# Database migrations are provided by `ef` -# Read-Evaluate-Print-Loop (REPL) is provided by `script`, see https://github.com/filipw/dotnet-script#repl -# 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 ./ -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} - -# COPY \ -# --chown=me:us \ -# ./package.json ./package-lock.json ./ -# RUN npm install - -# Create empty directories `/app/*/bin`, `/app/*/obj`, and -# `${HOME}/.vscode-server/*` for user `me` and group `us` to make the -# 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/logs \ - ./src/files && \ - mkdir --parents \ - ./src/bin \ - ./src/obj && \ - mkdir --parents \ - ./test/bin \ - ./test/obj && \ - mkdir --parents \ - ${HOME}/.vscode-server/extensions \ - ${HOME}/.vscode-server-insiders/extensions - -ENTRYPOINT ["/usr/bin/dumb-init", "--"] diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap deleted file mode 100644 index dc4cb6a5..00000000 --- a/backend/Dockerfile-bootstrap +++ /dev/null @@ -1,18 +0,0 @@ -# https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-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) -RUN \ - addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me - -USER me -WORKDIR /app - -ENV PATH=$PATH:/home/me/.dotnet/tools -RUN \ - dotnet tool install --global \ - dotnet-script diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production deleted file mode 100644 index 609da4c2..00000000 --- a/backend/Dockerfile-production +++ /dev/null @@ -1,59 +0,0 @@ -# Inspired by -# https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp -# and -# https://docs.docker.com/engine/examples/dotnetcore/ - -######### -# BUILD # -######### - -# https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-amd64 AS build - -COPY ./ /app - -WORKDIR /app - -RUN \ - dotnet restore -RUN \ - dotnet publish \ - --configuration release \ - --output out \ - --self-contained false \ - --no-restore - -########### -# RUNTIME # -########### - -# https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/aspnet:5.0-bullseye-slim-amd64 - -RUN \ - apt-get update && \ - apt-get install \ - --yes \ - --no-install-recommends \ - curl && \ - rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -WORKDIR /app - -COPY \ - --from=build \ - /app/out \ - ./ - -RUN \ - ln --symbolic Database.dll App.dll - -# See: https://github.com/dotnet/announcements/issues/20 -# Uncomment to enable globalization APIs -#ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false -#RUN apt-get install icu-libs -ENV LC_ALL=C.UTF-8 -ENV LANG=C.UTF-8 diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development new file mode 100644 index 00000000..00c1538d --- /dev/null +++ b/backend/Dockerfile.development @@ -0,0 +1,264 @@ +# syntax=docker/dockerfile:1.23 +# check=error=true;skip=SecretsUsedInArgOrEnv +# 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 +# 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 \ + 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 && \ + apt-get remove \ + --assume-yes \ + adduser && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + +############# +# 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 +# +# Tracing cannot only be done with `dotnet-trace`, which is connected to +# `dotnet-sos`, but also with LTTng, see +# https://docs.microsoft.com/en-us/dotnet/core/diagnostics/logging-tracing#logging-events +# and +# https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md#collecting-a-trace +# and +# https://stackoverflow.com/questions/55477684/how-to-configure-network-tracing-dotnet-core-for-httpclient-calls/58700657#58700657 +# and +# 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 \ + --assume-yes \ + --no-install-recommends \ + curl \ + editorconfig \ + gawk \ + gnupg2 \ + lldb \ + make \ + procps \ + 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 \ + --assume-yes \ + --no-install-recommends \ + bash \ + jq \ + neovim \ + && 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 \ + 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/* + +# 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 \ + me:us \ + "${HOME}" \ + && chown \ + me:us \ + "${HOME}/app" \ + && ln --symbolic "${HOME}/app" /app + +########### +# As `me` # +########### +USER me +WORKDIR /app + +# Install the tools specified in the manifest file `dotnet-tools.json`. +# For a comprehensive list of tools see https://github.com/natemcmaster/dotnet-tools +# Diagnostics is provided by `counters`, `dump`, and `trace`, see https://devblogs.microsoft.com/dotnet/introducing-diagnostics-improvements-in-net-core-3-0/ +# Code formatting is provided by `format` +# Code generation is provided by `aspnet-codegenerator` +# Database migrations are provided by `ef` +# Read-Evaluate-Print-Loop (REPL) is provided by `script`, see https://github.com/filipw/dotnet-script#repl +# 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 ./ +RUN dotnet tool restore \ + --tool-manifest ./dotnet-tools.json + +RUN mkdir ./src \ + && mkdir ./test +COPY --chown=me:us ./src/*.csproj ./src/ +COPY --chown=me:us ./test/*.Tests.csproj ./test/ + +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 + +# 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 +EOT + +# COPY \ +# --chown=me:us \ +# ./package.json ./package-lock.json ./ +# RUN npm install + +# Create empty directories `/app/*/bin`, `/app/*/obj`, and +# `${HOME}/.vscode-server/*` for user `me` and group `us` to make the +# 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 \ + ./src/bin \ + ./src/obj \ + && mkdir --parents \ + ./test/bin \ + ./test/obj \ + && mkdir --parents \ + "${HOME}/.nuget" \ + "${HOME}/.dotnet" \ + && mkdir --parents \ + "${HOME}/.vscode-server" \ + "${HOME}/.vscode-server-insiders" \ + && mkdir --parents \ + ${HOME}/.dotnet/corefx/cryptography/x509stores \ + && mkdir --parents \ + "${HOME}/.gnupg" \ + && chmod u=rwx,g=,o= \ + "${HOME}/.gnupg" \ + && echo "# use-keyboxd" \ + >"${HOME}/.gnupg/common.conf" \ + && echo "allow-loopback-pinentry" \ + >"${HOME}/.gnupg/gpg-agent.conf" + +ENV SHELL=/bin/bash + +ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production new file mode 100644 index 00000000..1c3ac24d --- /dev/null +++ b/backend/Dockerfile.production @@ -0,0 +1,132 @@ +# 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/dotnet/dotnet-docker/tree/master/samples/aspnetapp +# and +# https://docs.docker.com/engine/examples/dotnetcore/ + +######### +# BUILD # +######### + +# https://hub.docker.com/r/microsoft/dotnet-sdk +# 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 && \ +# apt-get upgrade --assume-yes && \ +# apt-get install \ +# --assume-yes \ +# --no-install-recommends \ +# gnupg2 && \ +# rm \ +# --recursive \ +# --force \ +# /var/lib/apt/lists/* + +COPY ./ /app + +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 +# hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. +RUN dotnet publish \ + --configuration release \ + --self-contained false \ + --no-restore + +########### +# RUNTIME # +########### + +# 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 +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 \ + && 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 \ + 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/* + +WORKDIR /app + +COPY --from=build /app/src/bin/Release/net10.0/publish ./ + +RUN mkdir --parents \ + /root/.dotnet/corefx/cryptography/x509stores \ + && mkdir --parents \ + /root/.gnupg \ + && chmod u=rwx,g=,o= \ + /root/.gnupg + +RUN ln --symbolic ./Database.dll ./App.dll + +# See: https://github.com/dotnet/announcements/issues/20 +# Uncomment to enable globalization APIs +#ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false +#RUN apt-get install icu-libs +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 + +ENV SHELL=/bin/bash diff --git a/backend/Makefile b/backend/Makefile index f8b72e91..f9c8f072 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,9 +1,13 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html +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].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help @@ -15,53 +19,62 @@ 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 -# ``` -test : ## Run tests - @echo "--------------------------------------------------" - @echo "Complete test list" - @echo "--------------------------------------------------" - dotnet test \ - --list-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 + +# 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 ARGS='--filter "FullyQualifiedName~LoggedInUser_CreatesComponent"'` + dotnet run \ + --project ./test/*.Tests.csproj \ + --list-tests \ + ${ARGS} +.PHONY : list + +# 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 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 ./*.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 \ 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 +# 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 \ @@ -73,9 +86,14 @@ analyze : ## Analyze with Roslynator dotnet tool run roslynator analyze .PHONY : analyze -play : ## Run facts in `Test.Play` (semi-alternative to the buggy `dotnet script` REPL) - dotnet test --filter "FullyQualifiedName~Test.Play" -.PHONY : play +# 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 --output "${OUTPUT}" ./*.sln +.PHONY : inspect + +editorconfig : ## Debug editorconfig + editorconfig ./.editorconfig +.PHONY : editorconfig # 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 @@ -93,41 +111,48 @@ 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 +# 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" ./*.sln` +# For syntax styling +# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./*.sln` +# For cleanup +# `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 ./*.sln + $(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 -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 @@ -135,7 +160,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 "{ \ @@ -152,62 +177,58 @@ 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` - dotnet ef \ - --project src/Database.csproj \ - migrations \ - add \ - ${NAME} -.PHONY : add-migration +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|FullyQualifiedName~EnsureDomainModelMatchesDatabaseSchema"' +.PHONY : check-database -migrate : ## Migrate database +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 \ - 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 + --project ./src/*.csproj \ + --startup-project ./src/*.csproj \ + migrations \ + 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 migration NAME=AddStatementToDataApproval` + old=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ + echo old=$$old && \ + dotnet ef \ + --project ./src/*.csproj \ + --startup-project ./src/*.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/Database.csproj \ + --project ./src/*.csproj \ + --startup-project ./src/*.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/*.csproj \ + --startup-project ./src/*.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 && \ + $(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/dotnet-tools.json b/backend/dotnet-tools.json index 380edb4c..1cf679f3 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,76 +3,95 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "5.0.2", + "version": "10.0.2", "commands": [ "dotnet-aspnet-codegenerator" - ] + ], + "rollForward": false }, "dotnet-counters": { - "version": "5.0.251802", + "version": "9.0.661903", "commands": [ "dotnet-counters" - ] + ], + "rollForward": false }, "dotnet-dump": { - "version": "5.0.251802", + "version": "9.0.661903", "commands": [ "dotnet-dump" - ] + ], + "rollForward": false }, "dotnet-ef": { - "version": "5.0.11", + "version": "10.0.9", "commands": [ "dotnet-ef" - ] - }, - "dotnet-format": { - "version": "5.1.250801", - "commands": [ - "dotnet-format" - ] + ], + "rollForward": false }, "dotnet-script": { - "version": "1.2.1", + "version": "2.0.1", "commands": [ "dotnet-script" - ] + ], + "rollForward": false }, "dotnet-trace": { - "version": "5.0.251802", + "version": "9.0.661903", "commands": [ "dotnet-trace" - ] + ], + "rollForward": false }, "dotnet-gcdump": { - "version": "5.0.251802", + "version": "9.0.661903", "commands": [ "dotnet-gcdump" - ] + ], + "rollForward": false }, "dotnet-sos": { - "version": "5.0.251802", + "version": "9.0.661903", "commands": [ "dotnet-sos" - ] + ], + "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.2.0", + "version": "0.12.0", "commands": [ "roslynator" - ] + ], + "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2021.2.2", + "version": "2026.1.3", "commands": [ "jb" - ] + ], + "rollForward": false + }, + "dotnet-project-licenses": { + "version": "2.7.1", + "commands": [ + "dotnet-project-licenses" + ], + "rollForward": false + }, + "dotnet-retest": { + "version": "1.0.0", + "commands": [ + "dotnet-retest" + ], + "rollForward": false }, - "dotnet-certificate-tool": { - "version": "2.0.6", + "dotnet-outdated-tool": { + "version": "4.8.1", "commands": [ - "certificate-tool" - ] + "dotnet-outdated" + ], + "rollForward": false } } } \ No newline at end of file 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/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/ComponentDataLoader.cs b/backend/src/ApiRequests/ComponentDataLoader.cs new file mode 100644 index 00000000..c585a9b7 --- /dev/null +++ b/backend/src/ApiRequests/ComponentDataLoader.cs @@ -0,0 +1,47 @@ +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 static class ComponentDataLoader +{ + private const string QueryFileName = "Components.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Component( + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } + + 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/DataDataLoader.cs b/backend/src/ApiRequests/DataDataLoader.cs new file mode 100644 index 00000000..217c4c36 --- /dev/null +++ b/backend/src/ApiRequests/DataDataLoader.cs @@ -0,0 +1,98 @@ +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 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 Id, + DataKind Kind, + ResourceTree ResourceTree + ); + + public sealed record Database( + Guid Id, + 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/DataFormatDataLoader.cs b/backend/src/ApiRequests/DataFormatDataLoader.cs new file mode 100644 index 00000000..762d630b --- /dev/null +++ b/backend/src/ApiRequests/DataFormatDataLoader.cs @@ -0,0 +1,50 @@ +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 static class DataFormatDataLoader +{ + private const string QueryFileName = "DataFormats.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record DataFormat( + [property: GraphQLIgnore] Guid Id, + string Name, + string? Extension, + string MediaType, + Uri? SchemaLocator + ) : IIdNode + { + public Guid Uuid => Id; + } + + 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/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/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs new file mode 100644 index 00000000..d4b2eea4 --- /dev/null +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -0,0 +1,47 @@ +using System; +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; + +public sealed class GetUserInfo( + AppSettings appSettings, + ApiRequestService apiRequestService +) +{ + public Uri GetEndpoint => + new UriBuilder(appSettings.MetabaseHostUri) { Path = "/connect/userinfo" }.Uri; + + public sealed record UserInfo( + Address? Address, + [GraphQLType] string? Email, + bool? EmailVerified, + string Name, // is not null because the OpenId Connect client asks for the scope `Profile` + [GraphQLType] string? PhoneNumber, + bool? PhoneNumberVerified, + IReadOnlyList? Roles, + string Sub, // Subject + [GraphQLType] string? Website + ); + + public sealed record Address( + string Formatted + ); + + /// + /// Request user info from Metabase. + /// + public Task Do( + CancellationToken cancellationToken) + { + return apiRequestService.PerformHttpGetRequest( + GetEndpoint, + cancellationToken + ); + } +} \ No newline at end of file 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/GraphQlRequestHelper.cs b/backend/src/ApiRequests/GraphQlRequestHelper.cs new file mode 100644 index 00000000..e2591ac8 --- /dev/null +++ b/backend/src/ApiRequests/GraphQlRequestHelper.cs @@ -0,0 +1,46 @@ +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) // 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() + ); + } + } +} \ 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..37aca9ae --- /dev/null +++ b/backend/src/ApiRequests/InstitutionDataLoader.cs @@ -0,0 +1,47 @@ +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 static class InstitutionDataLoader +{ + private const string QueryFileName = "Institutions.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Institution( + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } + + 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/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs new file mode 100644 index 00000000..2fe76f3a --- /dev/null +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Logging; +using Database.Services; +using GraphQL; +using Microsoft.Extensions.Logging; +using NodaTime; + +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 IsGnuPgFingerprintValid( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) +{ + private const string QueryFileName = "IsGnuPgFingerprintValid.graphql"; + + public Uri GetGraphQlEndpoint => + appSettings.MetabaseGraphQlEndpoint; + + public sealed record Institution( + bool HasGnuPgKeyFingerprint + ); + + private sealed record Data(Institution? Institution); + + public async Task Do( + string fingerprint, + Guid institutionId, + OffsetDateTime createdAt, + CancellationToken cancellationToken + ) + { + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, + new GraphQLRequest( + await GraphQlQueryHelpers.Construct(QueryFileName), + new + { + fingerprint, + institutionId, + createdAt + }, + "IsGnuPgFingerprintValid" + ), + cancellationToken + )); + 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/MethodDataLoader.cs b/backend/src/ApiRequests/MethodDataLoader.cs new file mode 100644 index 00000000..10d1f9ed --- /dev/null +++ b/backend/src/ApiRequests/MethodDataLoader.cs @@ -0,0 +1,47 @@ +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 static class MethodDataLoader +{ + private const string QueryFileName = "Methods.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Method( + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } + + 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/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs new file mode 100644 index 00000000..3387bbf7 --- /dev/null +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -0,0 +1,48 @@ +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 static class OpenIdConnectApplicationDataLoader +{ + private const string QueryFileName = "OpenIdConnectApplications.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record OpenIdConnectApplication( + Guid Uuid, + [property: GraphQLIgnore] string Id, + string? DisplayName + ) : IIdNode + { + public string ClientId => Id; + } + + 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/CalorimetricData.graphql b/backend/src/ApiRequests/Queries/CalorimetricData.graphql new file mode 100644 index 00000000..a709ba75 --- /dev/null +++ b/backend/src/ApiRequests/Queries/CalorimetricData.graphql @@ -0,0 +1,5 @@ +query CalorimetricData($id: Uuid!) { + calorimetricData(id: $id) { + ...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/ApiRequests/Queries/Components.graphql b/backend/src/ApiRequests/Queries/Components.graphql new file mode 100644 index 00000000..4c680835 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Components.graphql @@ -0,0 +1,10 @@ +query Components($ids: [Uuid!]!) { + connection: components(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + name + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql new file mode 100644 index 00000000..ffcb85f3 --- /dev/null +++ b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql @@ -0,0 +1,87 @@ +query CurrentUserOrApplication($databaseId: Uuid!) { + currentOpenIdConnectTokenClientId + 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/Data.graphql b/backend/src/ApiRequests/Queries/Data.graphql new file mode 100644 index 00000000..2038dc70 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Data.graphql @@ -0,0 +1,22 @@ +query Data($databaseId: Uuid!, $dataId: Uuid!, $dataKind: DataKind!) { + database(id: $databaseId) { + id: uuid + data(id: $dataId, kind: $dataKind) { + id: uuid + kind + resourceTree { + root { + vertexId + value { + hashValue + locator + dataFormat { + id: uuid + mediaType + } + } + } + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/DataFields.graphql b/backend/src/ApiRequests/Queries/DataFields.graphql new file mode 100644 index 00000000..7deefeb4 --- /dev/null +++ b/backend/src/ApiRequests/Queries/DataFields.graphql @@ -0,0 +1,109 @@ +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 + 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 { + 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/ApiRequests/Queries/DataFormats.graphql b/backend/src/ApiRequests/Queries/DataFormats.graphql new file mode 100644 index 00000000..5cb90fc3 --- /dev/null +++ b/backend/src/ApiRequests/Queries/DataFormats.graphql @@ -0,0 +1,14 @@ +query DataFormats($ids: [Uuid!]!) { + connection: dataFormats(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + name + extension + mediaType + schemaLocator + } + } + } +} + 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/GeometricData.graphql b/backend/src/ApiRequests/Queries/GeometricData.graphql new file mode 100644 index 00000000..cbd0aa6c --- /dev/null +++ b/backend/src/ApiRequests/Queries/GeometricData.graphql @@ -0,0 +1,5 @@ +query GeometricData($id: Uuid!) { + geometricData(id: $id) { + ...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..b85b2aff --- /dev/null +++ b/backend/src/ApiRequests/Queries/HygrothermalData.graphql @@ -0,0 +1,5 @@ +query HygrothermalData($id: Uuid!) { + hygrothermalData(id: $id) { + ...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/Institutions.graphql b/backend/src/ApiRequests/Queries/Institutions.graphql new file mode 100644 index 00000000..59dcc7ce --- /dev/null +++ b/backend/src/ApiRequests/Queries/Institutions.graphql @@ -0,0 +1,10 @@ +query Institutions($ids: [Uuid!]!) { + connection: institutions(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + name + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql new file mode 100644 index 00000000..ac5cff9a --- /dev/null +++ b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql @@ -0,0 +1,22 @@ +query IsGnuPgFingerprintValid( + $fingerprint: String! + $institutionId: Uuid! + $createdAt: DateTime! +) { + institution(id: $institutionId) { + hasGnuPgKeyFingerprint( + where: { + and: [ + { fingerprint: { equalTo: $fingerprint } } + { allowedAt: { lessThanOrEqualTo: $createdAt } } + { + or: [ + { forbiddenAt: { equalTo: null } } + { forbiddenAt: { greaterThan: $createdAt } } + ] + } + ] + } + ) + } +} 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/ApiRequests/Queries/Methods.graphql b/backend/src/ApiRequests/Queries/Methods.graphql new file mode 100644 index 00000000..d7ea1fd2 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Methods.graphql @@ -0,0 +1,10 @@ +query Methods($ids: [Uuid!]!) { + connection: methods(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + name + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql new file mode 100644 index 00000000..f53b6d6d --- /dev/null +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -0,0 +1,11 @@ +query OpenIdConnectApplications($ids: [String!]!) { + connection: openIdConnectApplications(where: { clientId: { in: $ids } }) { + edges { + node { + uuid + id: clientId + displayName + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/OpticalData.graphql b/backend/src/ApiRequests/Queries/OpticalData.graphql new file mode 100644 index 00000000..ac50eb55 --- /dev/null +++ b/backend/src/ApiRequests/Queries/OpticalData.graphql @@ -0,0 +1,5 @@ +query OpticalData($id: Uuid!) { + opticalData(id: $id) { + ...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..29ea2bf4 --- /dev/null +++ b/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql @@ -0,0 +1,5 @@ +query PhotovoltaicData($id: Uuid!) { + photovoltaicData(id: $id) { + ...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/ApiRequests/Queries/UpdateDatabase.graphql b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql new file mode 100644 index 00000000..fddff65e --- /dev/null +++ b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql @@ -0,0 +1,27 @@ +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 + } + } +} diff --git a/backend/src/ApiRequests/Queries/Users.graphql b/backend/src/ApiRequests/Queries/Users.graphql new file mode 100644 index 00000000..ebdea272 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Users.graphql @@ -0,0 +1,10 @@ +query Users($ids: [Uuid!]!) { + connection: users(where: { id: { in: $ids } }) { + 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/QueryByIdDataLoader.cs b/backend/src/ApiRequests/QueryByIdDataLoader.cs new file mode 100644 index 00000000..1652a2e4 --- /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 static class QueryByIdDataLoader +{ + public interface IIdNode + { + TId Id { get; } + } + + public sealed record Edge( + TNode Node + ); + + public sealed record Connection( + IReadOnlyCollection>? Edges + ); + + public interface IConnectionData + { + Connection? Connection { get; } + } + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + appSettings.MetabaseGraphQlEndpoint; + + 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 + { + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), + new GraphQLRequest( + await GraphQlQueryHelpers.Construct(queryFileNames), + new + { + ids + }, + null + ), + cancellationToken + )) + .Data + ?.Connection + ?.Edges + ?.ToDictionary( + _ => _.Node.Id, + _ => _.Node + ) ?? Enumerable.Empty().ToDictionary(_ => _.Id); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs new file mode 100644 index 00000000..08894bae --- /dev/null +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -0,0 +1,155 @@ +using System; +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 + ); +} + +/// +/// Request current user or institution from metabase. +/// +public sealed class QueryCurrentUserOrApplication( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) +{ + private const string QueryFileName = "CurrentUserOrApplication.graphql"; + + public static readonly CurrentUserOrApplication Empty = new(null, null, null); + + public Uri GetGraphQlEndpoint => + appSettings.MetabaseGraphQlEndpoint; + + public sealed record CurrentUser( + Guid Uuid, + string Name, + UserRepresentedInstitutionConnection RepresentedInstitutions, + UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions + ) + { + public bool IsAtLeastAssistantManagerOfDatabaseOperator() + { + return DatabaseOperatingRepresentedInstitutions.TotalCount > 0; + } + }; + + public sealed record UserRepresentedInstitutionConnection( + IReadOnlyList Edges, + int 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, + int TotalCount + ); + + public sealed record InstitutionManagedInstitutionEdge( + InstitutionManagedInstitutionNode Node + ); + + public sealed record InstitutionManagedInstitutionNode( + Guid Uuid, + string Name + ); + + public sealed record CurrentInstitution( + Guid Uuid, + string Name, + DatabaseOperatingDatabaseConnection DatabaseOperatingDatabases, + DatabaseOperatingManagedInstitutionConnection DatabaseOperatingManagedInstitutions + ) + { + public bool IsDatabaseOperator() + { + return DatabaseOperatingDatabases.TotalCount > 0 + || DatabaseOperatingManagedInstitutions.TotalCount > 0; + } + }; + + public sealed record DatabaseOperatingDatabaseConnection( + int TotalCount + ); + + public sealed record DatabaseOperatingManagedInstitutionConnection( + int TotalCount + ); + + public sealed record CurrentOpenIdConnectApplication( + string ClientId, + string? DisplayName, + CurrentInstitution Owner + ); + + public sealed record CurrentUserOrApplication( + string? currentOpenIdConnectTokenClientId, + CurrentUser? CurrentUser, + CurrentOpenIdConnectApplication? CurrentApplication + ) + { + public void Deconstruct( + out CurrentUser? currentUser, + out CurrentOpenIdConnectApplication? currentApplication + ) + { + currentUser = CurrentUser; + currentApplication = CurrentApplication; + } + }; + + public async Task Do( + CancellationToken cancellationToken + ) + { + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, + new GraphQLRequest( + await GraphQlQueryHelpers.Construct(QueryFileName), + new + { + databaseId = appSettings.DatabaseId + }, + "CurrentUserOrApplication" + ), + cancellationToken + )); + 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 new file mode 100644 index 00000000..992dd72a --- /dev/null +++ b/backend/src/ApiRequests/QueryData.cs @@ -0,0 +1,50 @@ +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 QueryData( + ApiRequestService apiRequestService, + ILogger logger +) +{ + public async Task> Do( + Uri databaseUrl, + Guid dataId, + string query, + CancellationToken cancellationToken) + + where TGraphQlData : class + { + var response = await apiRequestService.QueryGraphQl( + databaseUrl, + new GraphQLRequest( + query, + new { id = dataId } + ), + 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/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs new file mode 100644 index 00000000..4e842b34 --- /dev/null +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -0,0 +1,89 @@ +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 UpdateDatabase( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) +{ + private const string UpdateDatabaseFileName = "UpdateDatabase.graphql"; + + public Uri GetGraphQlEndpoint => + appSettings.MetabaseGraphQlEndpoint; + + public sealed record UpdateDatabaseInput( + Guid DatabaseId, + string Name, + string Description, + Uri Locator + ); + + public sealed record UpdateDatabasePayload( + DatabaseDataLoader.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 sealed record UpdateDatabaseData(UpdateDatabasePayload? UpdateDatabase); + + public async Task Do( + UpdateDatabaseInput updateDatabaseInput, + CancellationToken cancellationToken + ) + { + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, + new GraphQLRequest( + await GraphQlQueryHelpers.Construct( + UpdateDatabaseFileName + ), + new + { + input = updateDatabaseInput + }, + "UpdateDatabase" + ), + cancellationToken + )); + 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/ApiRequests/UserDataLoader.cs b/backend/src/ApiRequests/UserDataLoader.cs new file mode 100644 index 00000000..bffa2e9d --- /dev/null +++ b/backend/src/ApiRequests/UserDataLoader.cs @@ -0,0 +1,47 @@ +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 static class UserDataLoader +{ + private const string QueryFileName = "Users.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record User( + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } + + 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/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/AppSettings.cs b/backend/src/AppSettings.cs index 9defb15b..76397a29 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,52 +1,94 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited -namespace Database -{ - public sealed class AppSettings - { - public string Host { get; set; } - = ""; +using System; +using System.Text.Encodings.Web; +using Database.Data; +using Npgsql; - public string MetabaseHost { get; set; } - = ""; +namespace Database; - public string AccessToken { get; set; } - = ""; +public sealed record AppSettings +{ + private const string GraphQlPathSegment = "/graphql/"; - public LoggingSettings Logging { get; set; } - = new LoggingSettings(); + 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 + public Uri DatabaseGraphQlEndpoint => new UriBuilder(Uri) { Path = GraphQlPathSegment }.Uri; - public sealed class LoggingSettings - { - public bool EnableSensitiveDataLogging { get; set; } - = false; - } + 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 EmailSettings Email { get; set; } - = new EmailSettings(); + public Guid DatabaseId { get; init; } + public Guid OperatorId { get; init; } - public sealed class EmailSettings - { - public string SmtpHost { get; set; } - = ""; + public string VerificationCode { get; init; } = ""; - public int SmtpPort { get; set; } - = 0; - } + public OpenIdConnectClientSettings OpenIdConnectClient { get; init; } = new(); + public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; init; } = new(); + public DatabaseSettings Database { get; init; } = new(); + public OpenTelemetrySettings OpenTelemetry { get; init; } = new(); - public string OpenIdConnectClientSecret { get; set; } - = ""; + public sealed record OpenIdConnectClientSettings + { + public string Id { get; init; } = ""; + public string Secret { get; init; } = ""; + }; - public DatabaseSettings Database { get; set; } - = new DatabaseSettings(); + 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 Passphrase { get; init; } = ""; + public string Fingerprint { get; init; } = ""; + }; - public sealed class DatabaseSettings - { - public string ConnectionString { get; set; } - = ""; + public sealed record DatabaseSettings + { + 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; } = ""; - public string SchemaName { get; set; } - = ""; + 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; } - } -} \ No newline at end of file + }; + + public sealed record OpenTelemetrySettings + { + public string Host { private get; init; } = ""; + public int GrpcPort { private 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/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..a4247344 --- /dev/null +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -0,0 +1,204 @@ +using System; +using System.Linq; +using System.Security.Claims; +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; +using OpenIddict.Abstractions; +using OpenIddict.Client; +using OpenIddict.Validation.AspNetCore; + +namespace Database.Authentication; + +public sealed class AuthenticationHandler( + OpenIddictClientService openIddictClientService, + UserService userService +) +{ + private static async Task UpdateTokenValuesAsync( + HttpContext httpContext, + ClaimsPrincipal claimsPrincipal, + AuthenticationProperties authenticationProperties, + AuthenticationTokens freshTokens + ) + { + // Override the tokens using the values returned in the token response. + var properties = authenticationProperties.Clone(); + properties.UpdateTokenValue( + AuthenticationTokens.AccessTokenName, + freshTokens.AccessToken + ); + properties.UpdateTokenValue( + 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( + AuthenticationTokens.RefreshTokenName, + 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 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 = AuthenticationTokens.ExtractAccessToken(cookieAuthenticationResult); + var expirationDate = AuthenticationTokens.ExtractAccessTokenExpirationDate(cookieAuthenticationResult); + if (accessToken is not null + && expirationDate is not null + && TimeProvider.System.GetUtcNow() <= expirationDate?.Subtract(OpenIdConnectConstants.AccessTokenLifetime.Divide(3)) + ) + { + return accessToken; + } + var refreshToken = AuthenticationTokens.ExtractRefreshToken(cookieAuthenticationResult); + if (refreshToken is not null) + { + var refreshTokenAuthenticationResult = await openIddictClientService.AuthenticateWithRefreshTokenAsync( + new OpenIddictClientModels.RefreshTokenAuthenticationRequest + { + DisableUserInfo = true, + RefreshToken = refreshToken, + CancellationToken = cancellationToken, + } + ); + await UpdateTokenValuesAsync( + httpContext, + cookieAuthenticationResult.Principal, + cookieAuthenticationResult.Properties, + new AuthenticationTokens( + AccessToken: refreshTokenAuthenticationResult.AccessToken, + AccessTokenExpirationDate: refreshTokenAuthenticationResult.AccessTokenExpirationDate, + RefreshToken: refreshTokenAuthenticationResult.RefreshToken + ) + ); + return refreshTokenAuthenticationResult.AccessToken; + } + return null; + } + + public async Task AuthenticateAsync( + HttpContext httpContext, + CancellationToken cancellationToken + ) + { + 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 + // `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) + { + httpContext.SetBearerToken(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`. + // + // 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.SwitchUserOrApplicationAsync( + (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 }) + { + return metabaseAuthenticateResult; + } + return AuthenticateResult.Fail("Neither authenticated through cookie nor bearer token."); + } + + 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: claims, + authenticationType: "Metabase", + nameType: ClaimTypes.Name, + roleType: ClaimTypes.Role + ) + ), + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme + ) + ); + } +} \ No newline at end of file diff --git a/backend/src/Authentication/AuthenticationHelpers.cs b/backend/src/Authentication/AuthenticationHelpers.cs new file mode 100644 index 00000000..81cdd033 --- /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.Host == originOrReferer.Host; + } + + 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/AuthenticationTokens.cs b/backend/src/Authentication/AuthenticationTokens.cs new file mode 100644 index 00000000..6d245646 --- /dev/null +++ b/backend/src/Authentication/AuthenticationTokens.cs @@ -0,0 +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? RefreshToken +) +{ + 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/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/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs new file mode 100644 index 00000000..166cf511 --- /dev/null +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -0,0 +1,89 @@ +using System; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Database.GraphQl; +using HotChocolate; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Database.Authentication; + +public static partial class Log +{ + [LoggerMessage( + 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 + ) + { + httpContext.User = new ClaimsPrincipal(); // clear possibly-existing identities + 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/Authentication/OpenIdConnectConstants.cs b/backend/src/Authentication/OpenIdConnectConstants.cs new file mode 100644 index 00000000..f54e7697 --- /dev/null +++ b/backend/src/Authentication/OpenIdConnectConstants.cs @@ -0,0 +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 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/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/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 new file mode 100644 index 00000000..bbcb4668 --- /dev/null +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -0,0 +1,88 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using Database.ApiRequests; +using HotChocolate.Resolvers; +using HotChocolate; + +namespace Database.Authorization; + +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 SwitchUserOrApplicationAsync( + Func> handleUser, + Func> handleApplication, + CancellationToken cancellationToken + ) + { + return userService.SwitchUserOrApplicationAsync(handleUser, handleApplication, cancellationToken); + } + + public Task FetchCurrentUserOrApplicationAsync(CancellationToken cancellationToken) + { + return userService.FetchCurrentUserOrApplicationAsync(cancellationToken); + } + + public Task IsAuthenticated(CancellationToken cancellationToken) + { + return userService.SwitchUserOrApplicationAsync( + (user, _) => Task.FromResult(user is not null), + (application, _) => Task.FromResult(application is not null), + cancellationToken + ); + } + + public Task IsDatabaseOperator(CancellationToken cancellationToken) + { + return userService.SwitchUserOrApplicationAsync( + (user, _) => Task.FromResult( + user is not null + && user.IsAtLeastAssistantManagerOfDatabaseOperator() + ), + (application, _) => Task.FromResult( + application.Owner.IsDatabaseOperator() + ), + cancellationToken + ); + } + + public bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( + QueryCurrentUserOrApplication.CurrentUser currentUser, + Guid institutionId + ) + { + return currentUser.RepresentedInstitutions.Edges.Any(t => + ( + t.Role is QueryCurrentUserOrApplication.InstitutionRepresentativeRole.ASSISTANT + || t.Role is QueryCurrentUserOrApplication.InstitutionRepresentativeRole.OWNER + ) + && + ( + t.Node.Uuid == institutionId + || t.Node.ManagedInstitutions.Edges.Any(t => + t.Node.Uuid == institutionId + ) + ) + ); + } + + 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 86df2e05..c0f0901c 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,68 +1,348 @@ using System; -using System.IdentityModel.Tokens.Jwt; +using System.Collections.Generic; +using System.Net.Http; +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.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using NodaTime; +using OpenIddict.Abstractions; +using OpenIddict.Client; +using Quartz; +using Quartz.AspNetCore; -namespace Database.Configuration +namespace Database.Configuration; + +public static class AuthConfiguration { - public abstract class AuthConfiguration + private static readonly TimeSpan s_cookieExpirationTimeSpan = TimeSpan.FromDays(1); + + private static readonly Dictionary s_policyNameToOpenIdConnectScope = new() { - public static void ConfigureServices( - IServiceCollection services, - AppSettings appSettings - ) + { AuthorizationPolicies.ReadScopePolicy, OpenIdConnectScope.ReadApiScope }, + { AuthorizationPolicies.WriteScopePolicy, OpenIdConnectScope.WriteApiScope }, + }; + + private static void BootstrapCertificates(IClock clock) + { + using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); + try { - // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs - services - .AddAuthentication(_ => + store.Open(OpenFlags.ReadWrite); + { + var distinguishedName = OpenIdConnectConstants.SigningSubjectDistinguishedName; + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + validOnly: true + ); + if (certificates.Count is 0) + { + store.Add( + JwtSigningAndEncryptionCertificateRotationJob.CreateSigningCertificate( + distinguishedName, + clock + ) + ); + } + } + { + var distinguishedName = OpenIdConnectConstants.EncryptionSubjectDistinguishedName; + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + validOnly: true + ); + if (certificates.Count is 0) + { + store.Add( + JwtSigningAndEncryptionCertificateRotationJob.CreateEncryptionCertificate( + distinguishedName, + clock + ) + ); + } + } + } + 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, + IClock clock + ) + { + BootstrapCertificates(clock); + services.AddScoped(); + services.AddScoped(); + ConfigureAuthenticationAndAuthorizationServices(services); + ConfigureTaskScheduling(services, environment); + ConfigureOpenIddictServices(services, environment, appSettings); + AddAuthorizationServices(services); + } + + private static void AddAuthorizationServices( + IServiceCollection services + ) + { + services.AddScoped(); + } + + + 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(_ => { - _.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + // 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; }) + // The cookie is used by the database acting as client application through the + // authentication scheme `CookieAuthenticationDefaults.AuthenticationScheme`, that is, "Cookies". .AddCookie(_ => { _.AccessDeniedPath = "/unauthorized"; - _.LoginPath = "/users/login"; - _.LogoutPath = "/users/logout"; + _.LoginPath = "/connect/login"; + _.LogoutPath = "/connect/logout"; _.ReturnUrlParameter = "returnTo"; - _.ExpireTimeSpan = TimeSpan.FromMinutes(50); - _.SlidingExpiration = false; + _.ExpireTimeSpan = s_cookieExpirationTimeSpan; + _.SlidingExpiration = true; }) - .AddOpenIdConnect(_ => + .AddScheme< + CookieAndBearerTokenAuthenticationSchemeOptions, + CookieAndBearerTokenAuthenticationSchemeHandler + >( + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme, + _ => { } + ); + services.AddAuthorization(_ => { - // Note: these settings must match the application details - // inserted in the database at the server level. - _.ClientId = "testlab-solar-facades"; - _.ClientSecret = appSettings.OpenIdConnectClientSecret; + _.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); + } + ); + } + ); + } + } + ); + } - _.RequireHttpsMetadata = false; - _.GetClaimsFromUserInfoEndpoint = true; - _.SaveTokens = true; + 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 = OpenIdConnectConstants.DatabaseQuartzSchedulerId; + _.SchedulerName = "Database"; + 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.AddQuartzServer(_ => + _.WaitForJobsToComplete = true + ); + } - // Use the authorization code flow. - _.ResponseType = OpenIdConnectResponseType.Code; - _.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet; + private static void ConfigureOpenIddictServices( + IServiceCollection services, + IWebHostEnvironment environment, + AppSettings appSettings + ) + { + 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(); + }) + .AddValidation(_ => + { + // The validation handler uses OpenID Connect discovery to + // retrieve the issuer signing keys used to validate tokens. + _.SetIssuer(appSettings.MetabaseHostUri); + // Configure the audience accepted by this resource server. + _.AddAudiences(OpenIdConnectConstants.MetabaseClientId); + // 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(appSettings.OpenIdConnectClient.Id) + .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(); // 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(); // Authorization entry validation cannot be enabled when using introspection. + // Register the System.Net.Http integration. + _.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(_ => + { + _.AllowAuthorizationCodeFlow() + .AllowRefreshTokenFlow(); + + // Register the signing and encryption credentials. See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 + foreach (var encryptionCertificate in FindCertificates(OpenIdConnectConstants.EncryptionSubjectDistinguishedName)) + { + _.AddEncryptionCertificate(encryptionCertificate); + } + foreach (var signingCertificate in FindCertificates(OpenIdConnectConstants.SigningSubjectDistinguishedName)) + { + _.AddSigningCertificate(signingCertificate); + } - // 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 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(); + // .DisableTransportSecurityRequirement(); // https://documentation.openiddict.com/integrations/aspnet-core#transport-security-requirement - _.Scope.Add("email"); - _.Scope.Add("roles"); + // 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) + .ConfigureHttpClientHandler(handler => + { + if (environment.IsDevelopment()) + { + // https://documentation.openiddict.com/integrations/system-net-http#register-a-custom-httpclienthandler-configuration-delegate + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + } + }); - _.SecurityTokenValidator = new JwtSecurityTokenHandler + // Add a client registration matching the client application definition in the + // server project. + var clientRegistration = new OpenIddictClientRegistration { - // Disable the built-in JWT claims mapping feature. - InboundClaimTypeMap = new Dictionary() - }; + RegistrationId = OpenIdConnectConstants.MetabaseRegistrationId, + Issuer = appSettings.MetabaseHostUri, - _.TokenValidationParameters.NameClaimType = "name"; - _.TokenValidationParameters.RoleClaimType = "role"; + // 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 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, + 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 e47d13ae..e3cd29c2 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,280 +1,451 @@ using System; -using HotChocolate; -using HotChocolate.Types; +using System.Text.RegularExpressions; +using Database.Authentication; +using Database.Data; +using Database.Enumerations; +using Database.GraphQl; +using Database.GraphQl.Filters; +using Database.GraphQl.Scalars; +using HotChocolate.AspNetCore; +using HotChocolate.Configuration; +using HotChocolate.Data; using HotChocolate.Data.Filters; -using HotChocolate.Types.Pagination; -using Microsoft.AspNetCore.Authentication; +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.Descriptors.Configurations; +using HotChocolate.Types.NodaTime; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Http; 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 NodaTime; +using DurationType = HotChocolate.Types.NodaTime.DurationType; -namespace Database.Configuration +namespace Database.Configuration; + +public static class GraphQlConfiguration { - public static class GraphQlConfiguration + public static void ConfigureServices( + IServiceCollection services, + IWebHostEnvironment environment + ) { - public static void ConfigureServices( - IServiceCollection services, - IWebHostEnvironment environment - ) + // Automatic-Persisted-Queries Services + services + .AddMemoryCache(); + // GraphQL Server + var serverBuilder = services + .AddGraphQLServer(); + if (environment.IsDevelopment()) { - services.AddGraphQLServer() - // Types - .AddType() + // to debug exceptions like `System.InvalidOperationException: The name becomes immutable once it was assigned.` + serverBuilder.TryAddTypeInterceptor(); + } + serverBuilder + .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 + .RegisterDbContextFactory() + // .AddInstrumentation() + .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) // Extensions + .AddNodaTime() + .BindRuntimeType() + .AddTypeConverter(_ => _.ToTimeSpan()) + .AddTypeConverter(_ => Duration.FromTimeSpan(_)) + // .AddTypeConverter( + // _ => _.ToDateTimeOffset() + // ) + // .AddTypeConverter( + // _ => OffsetDateTime.FromDateTimeOffset(_) + // ) .AddProjections() .AddFiltering() - .AddSorting() + .AddSorting() + .AddConvention() + .AddQueryContext() .AddAuthorization() - .AddApolloTracing( - HotChocolate.Execution.Options.TracingPreference.OnDemand - ) // 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 = ... */ - } - ) - .ModifyRequestOptions(options => + .AddGlobalObjectIdentification(_ => + { + // _.MaxAllowedNodeBatchSize = 100; + _.EnsureAllNodesCanBeResolved = true; + } + ) + .ModifyOptions(_ => + { + // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Types/Configuration/Contracts/ISchemaOptions.cs + _.StrictValidation = true; + _.UseXmlDocumentation = false; + _.SortFieldsByName = true; + _.RemoveUnreachableTypes = false; + _.RemoveUnusedTypeSystemDirectives = true; + _.DefaultBindingBehavior = BindingBehavior.Implicit; + // options.DefaultFieldBindingFlags = FieldBindingFlags.InstanceAndStatic; + _.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; + } + ) + .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 = ...; */ - options.IncludeExceptionDetails = environment.IsDevelopment(); // Default is `Debugger.IsAttached`. - /* options.QueryCacheSize = ...; */ - /* options.UseComplexityMultipliers = ...; */ + _.ExecutionTimeout = TimeSpan.FromSeconds(120); + _.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. + _.AllowErrorHandlingModeOverride = true; + // options.QueryCacheSize = ...; + // options.UseComplexityMultipliers = ...; + // options.EnableSchemaFileSupport = true; + } + ) + .ModifyCostOptions(_ => + { + _.MaxFieldCost = 40000; + _.MaxTypeCost = 40000; } + ) + // Configure + // `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. + // .AddMaxExecutionDepthRule(5) + // .SetIntrospectionAllowedDepth(maxAllowedOfTypeDepth: 16, maxAllowedListRecursiveDepth: 1) + // .SetMaxAllowedValidationErrors(5) + // Subscriptions + /* .AddInMemorySubscriptions() */ + // Persisted queries + /* .AddFileSystemOperationDocumentStorage("./persisted_operations") */ + /* .UsePersistedOperationPipeline(); */ + .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => + { + await httpContext.RequestServices + .GetRequiredService() + .HandleAsync(httpContext, cancellationToken); + }) + .AddDiagnosticEventListener(_ => + new ErrorLoggingDiagnosticEventListener( + _.GetRequiredService>() ) - // 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 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 - } - ); - } + ) + // 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 MyUriType()) + .AddType(new AnyType("Any")) + .AddType() + .AddType() + .AddType() + .AddType() + .BindRuntimeType() + // 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() + // .AddCursorKeySerializer(new OffsetDateTimeCursorKeySerializer()) + .ModifyPagingOptions(_ => + { + _.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. + _.InferConnectionNameFromField = false; + } + ) + // Automatic Peristed Queries + .UseAutomaticPersistedOperationPipeline() + .AddInMemoryOperationDocumentStorage(); // Needed by the automatic persisted operation pipeline } - // See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions - public class CustomFilterConvention : FilterConvention + // + // 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 +public sealed class LoggingTypeInterceptor +: TypeInterceptor +{ + public override void OnBeforeInitialize(ITypeDiscoveryContext discoveryContext) { - 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(); - } + Console.WriteLine($"[INIT] Discovered type '{discoveryContext.Type.GetType().Name}'"); + } - // 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) - { - var nameString = base.GetTypeName(runtimeType); - if (!nameString.HasValue) - { - return nameString; - } - 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.Value, - "PropositionInput" - ), - "PropositionInput" - ), - "" - ), - "Uuid" - ), - "Floats" - ), - "Strings" - ), - "Float" - ), - "Float" - ), - "Data" - ); - } + public override void OnBeforeCompleteName(ITypeCompletionContext completionContext, TypeSystemConfiguration configuration) + { + Console.WriteLine($"[NAME] Finalizing name '{configuration.Name}' for type '{completionContext.Type.GetType().Name}'"); } - public static class FilterConventionDescriptorExtensions + public override void OnAfterCompleteType(ITypeCompletionContext completionContext, TypeSystemConfiguration configuration) { - // 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(); + Console.WriteLine($"[DONE] Completed type '{completionContext.Type.GetType().Name}' with name '{configuration.Name}'"); + } +} - // 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) +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 sealed partial class CustomFilterConvention : FilterConvention +{ + [GeneratedRegex(@$"({nameof(FilterInputType)}|{GraphQlConstants.FilterInputSuffix}|FilterType|FilterInput)$")] + private static partial Regex FilterInputSuffixesToReplaceRegex(); + + private readonly CustomNamingConventions _namingConventions = new(); + + protected override void Configure(IFilterConventionDescriptor descriptor) + { + descriptor.AddDefaults(); + // Use argument name `where` + descriptor.ArgumentName("where"); + // 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(context => + new QueryableComparableInClosedIntervalHandler(context.TypeConverter, context.InputParser) + ) + ) + ); + } + + // 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) + ); + + 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<>) + ) { - 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; + var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); + return $"{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; } - - // 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) + if (typeof(IComparableOperationFilterInputType).IsAssignableFrom(runtimeType) + && runtimeType.GenericTypeArguments.Length == 1 + && runtimeType.GetGenericTypeDefinition() + == typeof(ComparableOperationFilterInputType<>) + ) { - 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; + var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); + return $"Comparable{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; } - - // 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 + if (typeof(IListFilterInputType).IsAssignableFrom(runtimeType) + && runtimeType.GenericTypeArguments.Length == 1 + ) { - 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; + var genericType = runtimeType.GenericTypeArguments[0]; + return typeof(FilterInputType).IsAssignableFrom(genericType) + ? GetTypeName(genericType, plural: true) + : $"{_namingConventions.GetTypeName(genericType)}{pluralSuffix}"; } + var name = _namingConventions.GetTypeName(runtimeType); + if (typeof(FilterInputType).IsAssignableFrom(runtimeType) || !name.EndsWith("FilterInput", StringComparison.InvariantCulture)) + { + return $"{FilterInputSuffixesToReplaceRegex().Replace(name, string.Empty)}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + } + return $"{name}{pluralSuffix}"; + } +} + +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 + public static IFilterConventionDescriptor AddDefaults( + this IFilterConventionDescriptor descriptor) + { + return descriptor + .AddDefaultOperations() + .BindDefaultTypes() + .UseQueryableProvider(); } + + // Inspired by FilterConventionDescriptorExtensions#AddDefaultOperations + // 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 + ) + { + // Use speaking names for operations + descriptor.Operation(DefaultFilterOperations.Equals).Name("equalTo"); + descriptor.Operation(DefaultFilterOperations.NotEquals).Name("notEqualTo"); + 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.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.Any).Name("any"); + descriptor.Operation(DefaultFilterOperations.Like).Name("like"); + descriptor.Operation(DefaultFilterOperations.Data).Name("data"); + 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 + ) + { + 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() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType(); + } +} + +// See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sorting-conventions +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/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs new file mode 100644 index 00000000..42c8bbdd --- /dev/null +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -0,0 +1,53 @@ +using System; +using System.Net.Mime; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Database.Authentication; +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 RequireAntiforgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) +[ApiController] +public sealed class AntiforgeryController( + IAntiforgery antiforgeryService, + AuthenticationHandler authenticationHandler +) +: Controller +{ + private readonly CookieOptions _xsrfCookieOptions = + new() + { + HttpOnly = false, + SameSite = SameSiteMode.Strict + }; + + [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); + HttpContext.Response.Cookies.Append( + AntiforgeryConstants.CookieKey, + 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/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs new file mode 100644 index 00000000..bd788125 --- /dev/null +++ b/backend/src/Controllers/AuthenticationController.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Security.Claims; +using System.Threading; +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; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using OpenIddict.Abstractions; +using OpenIddict.Client.AspNetCore; + +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 sealed class AuthenticationController( + AppSettings appSettings, + ApplicationDbContext context +) : Controller +{ + private readonly Uri _issuer = appSettings.MetabaseHostUri; + 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. + ~AuthenticationController() + { + Dispose(false); + } + + [HttpGet("~/connect/login")] + public ActionResult LogIn(string? returnTo) + { + // 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.AbsoluteUri + } + ) + { + // Only allow local return URLs to prevent open redirect attacks. + RedirectUri = SanitizeReturnUrl(returnTo) + }, + OpenIddictClientAspNetCoreDefaults.AuthenticationScheme + ); + } + + [HttpPost("~/connect/logout")] + [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] + [RequireAntiforgeryToken] + 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). + 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(returnTo)); + } + + // 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.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] = AuthenticationTokens.ExtractIdentityToken(result) + } + ) + { + // Only allow local return URLs to prevent open redirect attacks. + RedirectUri = SanitizeReturnUrl(returnTo) + }, + 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] + [SuppressMessage("Style", "IDE0060")] + 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 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 is not { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + throw new InvalidOperationException("The external authorization data cannot be used for authentication."); + } + + 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}."); + 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. + var identity = new ClaimsIdentity( + 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. + // The claims are fetched from the userinfo endpoint of the authorization provider. + 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) + { + 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 + }; + + // 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( + // 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. + var user = await + context.Users.AsQueryable() + .SingleOrDefaultAsync( + u => u.Subject == subject, + cancellationToken + ); + if (user is null) + { + context.Users.Add( + new User( + subject, + name + ) + ); + } + else + { + user.Update(name); + } + 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), + 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] + [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. + 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? returnTo) + { + return + returnTo is not null && Url.IsLocalUrl(returnTo) + ? returnTo + : "/"; + } +} \ No newline at end of file diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs deleted file mode 100644 index b559e793..00000000 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.AspNetCore.Mvc; - -namespace Database.Controllers -{ - public class AuthenticationController : Controller - { - [HttpGet("~/users/login")] - public ActionResult LogIn() - { - return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme); - } - - [HttpGet("~/users/logout"), HttpPost("~/users/logout")] - public ActionResult LogOut() - { - // 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); - } - } -} \ No newline at end of file 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/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs deleted file mode 100644 index 7e31faf7..00000000 --- a/backend/src/Controllers/FileUploadController.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; -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 -{ - // 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 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)) - { - ModelState.AddModelError("File", - $"The request content is not multipart."); - return BadRequest(ModelState); - } - 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); - } - - 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().ConfigureAwait(false); - if (accessToken != _accessToken) - { - ModelState.AddModelError("AccessToken", $"The access token {accessToken} is invalid."); - return BadRequest(ModelState); - } - - 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 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); - } - } - - // 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); - } - - return Created(nameof(FileUploadController), null); - } - - 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 mediaType.Encoding; - } - } -} \ No newline at end of file diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs new file mode 100644 index 00000000..faa45193 --- /dev/null +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authentication; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Filters; +using Database.Services; +using Database.Utilities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +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}." + )] + 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 + ); +} + +// 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 GetHttpsResourcesController( + FileManager fileManager, + ILogger logger +) : Controller +{ + private bool _disposed; + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!_disposed) + { + // Dispose of resources held by this instance. + _disposed = true; + } + } + + // Disposable types implement a finalizer. + ~GetHttpsResourcesController() + { + Dispose(false); + } + + 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 in the media type of its data format.")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get( + [FromRoute] Guid id, + [FromServices] IDbContextFactory databaseContextFactory, + [FromServices] AccessPolicyService accessPolicyService, + [FromServices] IDataFormatByIdDataLoader dataFormatByIdDataLoader, + CancellationToken cancellationToken, + [FromRoute] string? extension = null + ) + { + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + var getHttpsResource = await databaseContext.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()) + { + 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.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path + ); + } + if (getHttpsResource.Data is null) + { + 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 + ); + } + if (!await accessPolicyService.ApplyAsync( + databaseContext => databaseContext.Data(getHttpsResource.Data.Kind).AsNoTracking() + .Where(_ => _.Id == getHttpsResource.DataId), + async policedData => + { + var node = await policedData.SingleOrDefaultAsync(cancellationToken); + return node is null + ? ([], false) + : ([node], true); + }, + databaseContextFactory, + cancellationToken + )) + { + return Unauthorized(); + } + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(getHttpsResource.DataFormatId); + if (dataFormat is null) + { + 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 + ); + } + getHttpsResource.UpdateFileExtension(dataFormat.Extension); + return PhysicalFile( + physicalPath: getHttpsResource.AbsoluteFilePath, + contentType: dataFormat.MediaType, + fileDownloadName: getHttpsResource.FileName, + enableRangeProcessing: true + ); + } + + private enum MyContentType + { + MULTIPART, + PIPE, + OTHER + } + + 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 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)] + [Produces(MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Upload( + [FromRoute] Guid id, + [FromServices] ApplicationDbContext context, + [FromServices] ResponseApprovalService responseApprovalService, + [FromServices] CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return Unauthorized(); + } + 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 (getHttpsResource.Data is null) + { + 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 + ); + } + Directory.CreateDirectory(GetHttpsResource.FilesDirectoryPath); + var contentType = + MultipartRequestHelper.IsMultipartContentType(Request.ContentType) ? MyContentType.MULTIPART + : Request.HasFormContentType ? MyContentType.PIPE + : MyContentType.OTHER; + switch (contentType) + { + case MyContentType.MULTIPART: + var boundary = MultipartRequestHelper.GetBoundary(Request.ContentType); + if (string.IsNullOrWhiteSpace(boundary)) + { + return Problem( + title: "Missing Boundary", + detail: "Missing boundary in multipart form data.", + statusCode: StatusCodes.Status400BadRequest, + instance: HttpContext.Request.Path + ); + } + await fileManager.SaveViaMultipartReaderAsync( + getHttpsResource.FilePath, + boundary, + Request.Body, + cancellationToken + ); + break; + case MyContentType.PIPE: + await fileManager.SaveViaPipeReaderAsync( + getHttpsResource.FilePath, + Request.BodyReader, + cancellationToken + ); + break; + default: + 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 + { + await getHttpsResource.Data.ExtractAndSetValuesFromFile( + getHttpsResource.FilePath, + getHttpsResource.DataFormatId + ); + } + catch (Exception exception) + { + logger.FailedToExtractAndSetValuesFromFile(getHttpsResource.FilePath, getHttpsResource.Id, getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); + 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); + 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, getHttpsResource.Data.Kind, exception); + errors.Add($"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}: '{exception.Message}'"); + } + if (errors.Count > 0) + { + 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 CreatedAtRoute( + ConstructGetActionRouteName(getHttpsResource), + CreateGetActionRouteValues(getHttpsResource), + null + ); + } +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/AccessPolicyBase.cs b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs new file mode 100644 index 00000000..8bbb5cf4 --- /dev/null +++ b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs @@ -0,0 +1,49 @@ +using System; +using Database.Extensions; +using EntityFrameworkCore.Projectables; +using NodaTime; + +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 IsAlwaysAllowed => UpperAccessLimitPerTimeDuration is null; + + [Projectable] + public bool IsWithinAccessLimitInTimeSpan => 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 + || DateTimeOffset.UtcNow <= AccessCountSinceStartTime.StartTime + UpperAccessLimitPerTimeDuration.Duration; + + 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/CountSinceTime.cs b/backend/src/Data/AccessPolicies/CountSinceTime.cs new file mode 100644 index 00000000..580bc3ac --- /dev/null +++ b/backend/src/Data/AccessPolicies/CountSinceTime.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data.AccessPolicies; + +[Owned] +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..ecf9f211 --- /dev/null +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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. + +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 +{ + 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(); + } + + 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 for the global data access policy, all `*DataId`s are null + [Projectable] + public Guid? DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? null; + + public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; + + [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 + : null; + + [Projectable] + public Guid? GetDataId(Database.Enumerations.DataKind dataKind) => + dataKind switch + { + 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(Database.Enumerations.DataKind dataKind) => + dataKind switch + { + 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}"), + }; + + 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; } = []; + + [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 IsEveryoneAllowed => IsAccessAllowed(null, null, null); + + [Projectable] + public bool IsAccessAllowed( + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId + ) => + ( + Combinator == LogicalCombinator.ALL && ( + ( + UserAccessPolicies.Count == 0 + || UserAccessPolicies.Any(_ => + _.IsAccessAllowed(userId) + ) + ) + && + ( + InstitutionAccessPolicies.Count == 0 + || InstitutionAccessPolicies.Any(_ => + _.IsAccessAllowed(institutionIds) + ) + ) + && + ( + OpenIdConnectApplicationAccessPolicies.Count == 0 + || OpenIdConnectApplicationAccessPolicies.Any(_ => + _.IsAccessAllowed(openIdConnectClientId) + ) + ) + ) + ) + || + ( + Combinator == LogicalCombinator.SOME && ( + UserAccessPolicies.Any(_ => + _.IsAccessAllowed(userId) + ) + || + InstitutionAccessPolicies.Any(_ => + _.IsAccessAllowed(institutionIds) + ) + || + OpenIdConnectApplicationAccessPolicies.Any(_ => + _.IsAccessAllowed(openIdConnectClientId) + ) + ) + ); + + public void Reset() + { + Combinator = LogicalCombinator.ALL; + UserAccessPolicies.Clear(); + InstitutionAccessPolicies.Clear(); + OpenIdConnectApplicationAccessPolicies.Clear(); + } + + public void Configure( + LogicalCombinator combinator + ) + { + Combinator = combinator; + } +} \ 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..89591565 --- /dev/null +++ b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; + +namespace Database.Data.AccessPolicies; + +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; + + [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 new file mode 100644 index 00000000..3931c904 --- /dev/null +++ b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; + +namespace Database.Data.AccessPolicies; + +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; + + [Projectable] + public bool IsAccessAllowed(string? openIdConnectClientId) => + openIdConnectClientId != null + && ClientId == openIdConnectClientId + && IsWithinAccessLimitInTimeSpan; +} \ 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..159145d1 --- /dev/null +++ b/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data.AccessPolicies; + +[Owned] +public sealed record UpperLimitPerDuration( + uint UpperLimit, + TimeSpan? Duration +); \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs b/backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs new file mode 100644 index 00000000..143c08eb --- /dev/null +++ b/backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs @@ -0,0 +1,14 @@ +using System; +using NodaTime; +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/UserAccessPolicy.cs b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs new file mode 100644 index 00000000..a4a928e9 --- /dev/null +++ b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; + +namespace Database.Data.AccessPolicies; + +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; + + [Projectable] + public bool IsAccessAllowed(Guid? userId) => + userId != null + && UserId == userId + && IsWithinAccessLimitInTimeSpan; +} \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 67567022..442da3fa 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,104 +1,1061 @@ +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.Extensions; +using GreenDonut.Data; +using Laraue.EfCoreTriggers.Common.Extensions; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Npgsql; -using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Database.GraphQl; +using Database.Data.Extensions; +using Database.Data.AccessPolicies; +using Npgsql.EntityFrameworkCore.PostgreSQL.ValueGeneration; -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) - [Owned] - public sealed class ApplicationDbContext - : DbContext - { - static 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"; + 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"; + internal const string StandardizerTypeName = "standardizer"; + + public ApplicationDbContext( + DbContextOptions options, + IClock clock + ) + : 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 ? DefaultSchemaName : schemaNameOptions.SchemaName; + _clock = clock; + } + + // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset + 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!; + + public IQueryable GetHttpsResourcesWithData => + GetHttpsResources.AsQueryable() + .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!; + + // 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(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + base.ConfigureConventions(configurationBuilder); + } + + private sealed class DateTimeUtcValueConverter : ValueConverter + { + public DateTimeUtcValueConverter() + : base( + v => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime(), + v => DateTime.SpecifyKind(v, DateTimeKind.Utc) + ) { - RegisterEnumerations(); } + } - private static void RegisterEnumerations() + private sealed class DateTimeOffsetUtcValueConverter : ValueConverter + { + public DateTimeOffsetUtcValueConverter() + : base( + v => v.ToUniversalTime(), + v => v + ) { - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - NpgsqlConnection.GlobalTypeMapper.MapEnum(); } + } - private static void CreateEnumerations(ModelBuilder builder) + private sealed class OffsetDateTimeUtcValueConverter : ValueConverter + { + public OffsetDateTimeUtcValueConverter() + : base( + v => v.WithOffset(Offset.Zero), + v => v + ) { - // 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. - builder.HasPostgresEnum("public"); } + } - private static - EntityTypeBuilder - ConfigureEntity( - EntityTypeBuilder builder - ) - where TEntity : Data.Entity + 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) { - // 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 - .UseXminAsConcurrencyToken(); - return builder; + 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; + } } + } - private static void ConfigureIdentityEntities( - ModelBuilder builder - ) + public IQueryable Data(DataKind dataKind) + { + return dataKind switch { - // https://stackoverflow.com/questions/19902756/asp-net-identity-dbcontext-confusion/35722688#35722688 - } + 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}."), + }; + } - private readonly string _schemaName; + public Task GetDataAsync(Guid id, DataKind dataKind, CancellationToken cancellationToken) + { + return Data(dataKind).SingleOrDefaultAsync(_ => _.Id == id, cancellationToken); + } + + public IAsyncEnumerable GetAllDataAsync( + Expression>? where = null, + QueryContext? queryContext = null + ) + { + where ??= _ => true; + return ( + CalorimetricData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ) + .Union( + GeometricData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ) + .Union( + HygrothermalData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ) + .Union( + LifeCycleData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ) + .Union( + OpticalData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ) + .Union( + PhotovoltaicData.AsQueryable() + .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToAsyncEnumerable() + ); + } - // 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!; + 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( + _ => _.Approval, + approval => + { + approval + .Property(a => a.Variables) + .HasDefaultValueSql("'{}'"); + } + ); + builder + .OwnsMany( + _ => _.Approvals, + approvals => + { + approvals + .Property(a => a.Variables) + .HasDefaultValueSql("'{}'"); + } + ); + return builder; + } - public ApplicationDbContext( - DbContextOptions options + private + EntityTypeBuilder + ConfigureGetHttpsResource( + EntityTypeBuilder builder + ) + { + // add partial indexes, see https://www.postgresql.org/docs/18/indexes-partial.html + builder + .HasIndex(_ => new { _.CalorimetricDataId }) + .HasFilter( + $""" + {nameof(GetHttpsResource.CalorimetricDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ ) - : base(options) - { - var schemaNameOptions = options.FindExtension(); - _schemaName = schemaNameOptions is null ? "database" : schemaNameOptions.SchemaName; - } + .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 { _.LifeCycleDataId }) + .HasFilter( + $""" + {nameof(GetHttpsResource.LifeCycleDataId).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) + .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 {_schemaName}.{GetHttpsResource.TableName.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 + 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.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; + """ + ) + ) + ); + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...`. + builder + .AfterDelete(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; + } - 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() + 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).Enquote()} + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + """ + ) ) - .ToTable("get_https_resource"); - ConfigureEntity( - builder.Entity() + ); + // 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( + 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 {_schemaName}.{DataAccessPolicy.TableName.Enquote()} + ({dataIdColumn.Enquote()}, {nameof(DataAccessPolicy.Combinator).Enquote()}) + VALUES (NEW.{nameof(IEntity.Id).Enquote()}, '{LogicalCombinator.ALL.ToString().ToLowerInvariant()}') + ON CONFLICT ({dataIdColumn.Enquote()}) WHERE {dataIdColumn.Enquote()} IS NOT NULL DO NOTHING; + """ + ) + // an alternative is + // .InsertIfNotExists( + // insertedData => new DataAccessPolicy + // { + // CalorimetricDataId = insertedData.Id, + // Combinator = LogicalCombinator.ALL + // }, + // data => new { data.CalorimetricDataId } + // ) ) - .ToTable("calorimetric_data"); - ConfigureEntity( - builder.Entity() + ); + // 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; + } + + private + EntityTypeBuilder + ConfigureCalorimetricData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.CalorimetricData) + .HasForeignKey(_ => _.CalorimetricDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .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 + EntityTypeBuilder + ConfigureGeometricData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.GeometricData) + .HasForeignKey(_ => _.GeometricDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .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 + EntityTypeBuilder + ConfigureHygrothermalData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.HygrothermalData) + .HasForeignKey(_ => _.HygrothermalDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .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 + EntityTypeBuilder + ConfigureLifeCycleData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.LifeCycleData) + .HasForeignKey(_ => _.LifeCycleDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .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 + EntityTypeBuilder + ConfigureOpticalData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.OpticalData) + .HasForeignKey(_ => _.OpticalDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.OpticalData) + .HasForeignKey(_ => _.OpticalDataId) + .OnDelete(DeleteBehavior.Cascade); + builder.OwnsMany( + data => data.CielabColors + ).ToTable( + color => + { + color.HasCheckConstraint( + $"CK_{nameof(OpticalData)}_{nameof(CielabColor)}s_{nameof(CielabColor.LStar)}", + $"{nameof(CielabColor.LStar).Enquote()} >= 0.0 AND {nameof(CielabColor.LStar).Enquote()} <= 100.0" + ); + } + ); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.OpticalData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.OpticalDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.OpticalData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.OpticalDataId) + ); + return builder; + } + + private + EntityTypeBuilder + ConfigurePhotovoltaicData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.PhotovoltaicData) + .HasForeignKey(_ => _.PhotovoltaicDataId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .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 + 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; + """ + ) ) - .ToTable("hygrothermal_data"); - ConfigureEntity( - builder.Entity() + ); + 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; + """ + ) ) - .ToTable("optical_data"); - ConfigureEntity( - builder.Entity() + ); + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...`. + builder + .AfterDelete(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 {_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 in the same transaction.'; + END IF; + """ + ) ) - .ToTable("photovoltaic_data"); + ); + return builder; + } + + private static + EntityTypeBuilder + ConfigureUserAccessPolicy( + EntityTypeBuilder builder + ) + { + 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; + } + + 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 + ConfigureGetHttpsResource( + modelBuilder.Entity() + ).ToTable( + GetHttpsResource.TableName, + _ => + { + _.HasCheckConstraint( + $"CK_{nameof(GetHttpsResource)}_Root_Or_Child", + $""" + ({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.DataIdFieldAndDataTableNames.Select(_ => _.Field.Enquote()))}) = 1" + ); + foreach (var triggerName in GetHttpsResource.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); + ConfigureCalorimetricData( + ConfigureData( + modelBuilder.Entity() + ) + ).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, + _ => + { + foreach (var triggerName in global::Database.Data.GeometricData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); + ConfigureHygrothermalData( + ConfigureData( + modelBuilder.Entity() + ) + ).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, + _ => + { + foreach (var triggerName in global::Database.Data.LifeCycleData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); + ConfigureOpticalData( + ConfigureData( + modelBuilder.Entity() + ) + ).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, + _ => + { + foreach (var triggerName in global::Database.Data.PhotovoltaicData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); + 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"); + ConfigureInstitutionAccessPolicy( + modelBuilder.Entity() + ).ToTable("institution_access_policy"); + ConfigureOpenIdConnectApplicationAccessPolicy( + modelBuilder.Entity() + ).ToTable("open_id_connect_application_access_policy"); + 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("uuidv7()") + .HasValueGenerator(); + // 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/AppliedMethod.cs b/backend/src/Data/AppliedMethod.cs index 1311db11..c9e68565 100644 --- a/backend/src/Data/AppliedMethod.cs +++ b/backend/src/Data/AppliedMethod.cs @@ -2,32 +2,25 @@ using System.Collections.Generic; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class AppliedMethod( + Guid methodId + ) { - [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; - } - - // `DbContext` needs this constructor without owned entities. - public AppliedMethod( - Guid methodId - ) - { - MethodId = methodId; - } + Arguments = arguments; + Sources = sources; } + + public Guid MethodId { get; private set; } = methodId; + public ICollection Arguments { get; private set; } = []; + public ICollection Sources { get; private set; } = []; } \ 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 affdf121..e7927efb 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,71 +1,110 @@ 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 -{ - public sealed class CalorimetricData +namespace Database.Data; + +public sealed class CalorimetricData : DataX +{ + public const string TableName = "calorimetric_data"; + + public static readonly DataKind TheKind = DataKind.CALORIMETRIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + public CalorimetricData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + AppliedMethod appliedMethod, + double[] gValues, + double[] uValues + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) { - [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( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + double[] gValues, + double[] uValues + ) : base( + userId, + 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; - } + [NotMapped] + public override DataKind Kind => TheKind; + + [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] + public override ICollection Resources { get; } = []; - // `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 - ) + [InverseProperty(nameof(DataAccessPolicy.CalorimetricData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + + 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 = gValues; - UValues = uValues; + 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/CielabColor.cs b/backend/src/Data/CielabColor.cs index 425056d7..0790b6c1 100644 --- a/backend/src/Data/CielabColor.cs +++ b/backend/src/Data/CielabColor.cs @@ -1,23 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Database.Enumerations; +using Database.Json; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class CielabColor( + double lStar, + double aStar, + double bStar, + CalorimetricObserver? observer = null, + Illuminant? illuminant = null + ) { - [Owned] - public sealed class CielabColor - { - public double LStar { get; private set; } - public double AStar { get; private set; } - public double BStar { get; private set; } + [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 CielabColor( - double lStar, - double aStar, - double bStar - ) - { - LStar = lStar; - AStar = aStar; - BStar = 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..cc47d0dd --- /dev/null +++ b/backend/src/Data/ColorRenderingIndex.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Database.Enumerations; +using Database.Json; +using Microsoft.EntityFrameworkCore; + +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/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index f026836a..c0399f43 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -1,28 +1,20 @@ using System; -using Microsoft.EntityFrameworkCore; using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using NodaTime; -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( - Guid dataId, - DateTime dataTimestamp, - DataKind dataKind, - Guid databaseId - ) - { - DataId = dataId; - DataTimestamp = dataTimestamp; - DataKind = dataKind; - DatabaseId = databaseId; - } - } -} +[Owned] +public sealed class CrossDatabaseDataReference( + Guid dataId, + OffsetDateTime dataTimestamp, + DataKind dataKind, + Guid databaseId + ) +{ + public Guid DataId { get; private set; } = dataId; + 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 83d59b7d..e223da1b 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,34 +1,58 @@ using System; +using System.Text.Json; +using Database.GraphQl.Scalars; +using HotChocolate; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; +using NodaTime; -namespace Database.Data -{ - [Owned] - public sealed class DataApproval - : IApproval - { - 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; } +namespace Database.Data; - public DataApproval( - DateTime timestamp, - string signature, - string keyFingerprint, - string query, - string response, - Guid approverId +[Owned] +public sealed class DataApproval( + OffsetDateTime timestamp, + string signature, + string keyFingerprint, + string query, + JsonElement variables, + string message, + 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( + OffsetDateTime timestamp, + string signature, + string keyFingerprint, + string query, + JsonElement variables, + string message, + Guid approverId + ) + : this( + timestamp, + signature, + keyFingerprint, + query, + variables, + message, + approverId, + null! // EF Core will set this owned navigation property after construction. ) - { - Timestamp = timestamp; - Signature = signature; - KeyFingerprint = keyFingerprint; - Query = query; - Response = response; - ApproverId = approverId; - } + { } -} + + public Guid ApproverId { get; private set; } = 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; +} \ No newline at end of file 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 68ed4fbf..6ffe4ea9 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,70 +1,122 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; +using Database.Data.AccessPolicies; +using Database.Enumerations; -namespace Database.Data +namespace Database.Data; + +public abstract class DataX : AuditableEntity, IData { - 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; } + protected static string AssertExistenceOfRootResourceTriggerName(DataKind kind) => + $"{kind.ToString().ToLowerInvariant()}_assert_existence_of_root_resource"; - // 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(); + protected static string CreateDataAccessPolicyIfNecessaryTriggerName(DataKind kind) => + $"create_{kind.ToString().ToLowerInvariant()}_access_policy_if_necessary"; - protected DataX( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval - ) + 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, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + AppliedMethod appliedMethod + ) : this( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt ) - { - AppliedMethod = appliedMethod; - Approvals = approvals; - // Approval = approval; - } + { + 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 void Update( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + DateTimeOffset createdAt, + Guid creatorId + ) + { + Locale = locale; + ComponentId = componentId; + Name = name; + Description = description; + Warnings = warnings; + CreatedAt = createdAt; + CreatorId = creatorId; } -} + + public void Publish() + { + PublishingState = PublishingState.PUBLISHED; + } + + public void Retract() + { + PublishingState = PublishingState.RETRACTED; + } + + 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; } = []; + public ResponseApproval? Approval { get; set; } + + // 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 abstract DataAccessPolicy? AccessPolicy { get; set; } + + [NotMapped] + public abstract DataKind Kind { get; } + + public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; + + public abstract Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ); +} \ No newline at end of file diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index 60871ed1..7a42f435 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -1,21 +1,32 @@ using System; +using System.Threading; 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( + 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, + CancellationToken cancellationToken + ) { - 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(); + await Task.FromResult(0); } } \ No newline at end of file diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index 69e311e7..d831fdc6 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -1,21 +1,25 @@ -using Guid = System.Guid; -// using System.ComponentModel.DataAnnotations.Schema; +using System; -namespace Database.Data +namespace Database.Data; + +public abstract class Entity + : IEntity { - public abstract class Entity - : IEntity + public Entity() + { + } + + public Entity(Guid id) { - public Guid Id { get; private set; } + Id = id; + } - // [NotMapped] - // public Guid Uuid { get => Id; } + public Guid Id { get; init; } - public uint xmin { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html + // [NotMapped] + // public Guid Uuid { get => Id; } - protected Entity( - ) - { - } - } + // 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/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 d67ab313..5b86a014 100644 --- a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs +++ b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs @@ -3,57 +3,51 @@ 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(string schemaName) + : IDbContextOptionsExtension { - public sealed class SchemaNameOptionsExtension - : IDbContextOptionsExtension + public string SchemaName { get; } = schemaName; + + public DbContextOptionsExtensionInfo Info + => new SchemaNameExtensionInfo(this); + + public void ApplyServices(IServiceCollection services) { - public DbContextOptionsExtensionInfo Info - => new SchemaNameExtensionInfo(this); + } - public string SchemaName { get; } + public void Validate(IDbContextOptions options) + { + } - public SchemaNameOptionsExtension(string schemaName) - { - SchemaName = schemaName; - } + public sealed class SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) + : DbContextOptionsExtensionInfo(extension) + { + public override bool IsDatabaseProvider + => false; + + public override string LogFragment + => $"{nameof(Extension.SchemaName)}={Extension.SchemaName}"; + + public new SchemaNameOptionsExtension Extension => (SchemaNameOptionsExtension)base.Extension; - public void ApplyServices(IServiceCollection services) + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) { + return true; } - public void Validate(IDbContextOptions options) + public override int GetServiceProviderHashCode() { + return 0; } - public sealed class SchemaNameExtensionInfo - : DbContextOptionsExtensionInfo + public override void PopulateDebugInfo( + IDictionary debugInfo + ) { - public override bool IsDatabaseProvider - => false; - - public override string LogFragment - => $"{nameof(Extension.SchemaName)}={Extension.SchemaName}"; - - public new SchemaNameOptionsExtension Extension => (SchemaNameOptionsExtension)base.Extension; - - public SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) - : base(extension) - { - } - - public override long GetServiceProviderHashCode() - { - return 0; - } - - public override void PopulateDebugInfo( - IDictionary debugInfo - ) - { - debugInfo[$"Infrastructure.Data:${nameof(SchemaNameOptionsExtension.SchemaName)}"] - = Extension.SchemaName; - } + 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..afa1a8ec 100644 --- a/backend/src/Data/FileMetaInformation.cs +++ b/backend/src/Data/FileMetaInformation.cs @@ -1,21 +1,14 @@ using System; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class FileMetaInformation - { - public string[] Path { get; private set; } - public Guid DataFormatId { get; private set; } +namespace Database.Data; - public FileMetaInformation( - string[] path, - Guid dataFormatId - ) - { - Path = path; - DataFormatId = dataFormatId; - } - } -} +[Owned] +public sealed class FileMetaInformation( + string[] path, + Guid dataFormatId + ) +{ + 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/GeometricData.cs b/backend/src/Data/GeometricData.cs new file mode 100644 index 00000000..e50be906 --- /dev/null +++ b/backend/src/Data/GeometricData.cs @@ -0,0 +1,117 @@ +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; + +public sealed class GeometricData + : DataX +{ + public const string TableName = "geometric_data"; + + public static readonly DataKind TheKind = DataKind.GEOMETRIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + public GeometricData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + AppliedMethod appliedMethod, + double[] widths, + double[] heights, + double[] thicknesses + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) + { + Widths = widths; + Heights = heights; + Thicknesses = thicknesses; + } + + public GeometricData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + double[] widths, + double[] heights, + double[] thicknesses + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + Widths = widths; + Heights = heights; + Thicknesses = thicknesses; + } + + [NotMapped] + public override DataKind Kind => TheKind; + + [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; } + + 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/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 8fc9bf07..77988749 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -1,141 +1,329 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Database.Extensions; +using Database.Utilities; +using EntityFrameworkCore.Projectables; -namespace Database.Data +namespace Database.Data; + +public sealed class GetHttpsResource +: AuditableEntity { - public sealed class GetHttpsResource - : Data.Entity + public const string FilesDirectoryPath = "./files/"; + public const string TableName = "get_https_resource"; + + public const string DataIdsMustMatchTriggerName = $"{TableName}_data_ids_must_match"; + public const string DataIdCannotChangeTriggerName = $"{TableName}_data_id_cannot_change"; + public const string RootCanOnlyBeDeletedAlongsideItsDataTriggerName = $"resource_root_can_only_deleted_alongside_its_data"; + public static readonly ImmutableArray TriggerNames = [ + DataIdsMustMatchTriggerName, + DataIdCannotChangeTriggerName, + RootCanOnlyBeDeletedAlongsideItsDataTriggerName + ]; + + 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 + public GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + string? fileExtension + ) + { + Description = description; + HashValue = hashValue; + DataFormatId = dataFormatId; + FileExtension = fileExtension; + } + + /// + /// Construct a root resource. + /// + public GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + string? fileExtension, + ICollection archivedFilesMetaInformation + ) + : this( + description, + hashValue, + dataFormatId, + fileExtension + ) { - public static string ConstructVertexId(Guid id) + ParentId = null; + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + AppliedConversionMethod = null; + // The data ID is set by EF Core. + } + + /// + /// Construct a child resource. + /// + public GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + string? fileExtension, + Guid? calorimetricDataId, + Guid? geometricDataId, + Guid? hygrothermalDataId, + Guid? lifeCycleDataId, + Guid? opticalDataId, + Guid? photovoltaicDataId, + Guid parentId, + ICollection archivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethod appliedConversionMethod + ) + : this( + description, + hashValue, + dataFormatId, + fileExtension + ) + { + CalorimetricDataId = calorimetricDataId; + GeometricDataId = geometricDataId; + HygrothermalDataId = hygrothermalDataId; + LifeCycleDataId = lifeCycleDataId; + OpticalDataId = opticalDataId; + PhotovoltaicDataId = photovoltaicDataId; + ParentId = parentId; + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + AppliedConversionMethod = appliedConversionMethod; + AssertThatExactlyOneDataIdIsNonNull(); + } + + 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) { - return id.ToString("D").Base64Encode(); + throw new InvalidOperationException("There is more than 1 non-null data ID."); } + } - 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 string? Description { get; private set; } + public string HashValue { get; private set; } + public Guid DataFormatId { get; private set; } + public string? FileExtension { 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 { get => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; } - [NotMapped] - public IData? Data { get => CalorimetricData ?? HygrothermalData ?? OpticalData ?? PhotovoltaicData as IData; } + public string FileName => + Id.ToString("D") + + (FileExtension is null ? "" : $".{FileExtension}"); - public Guid? CalorimetricDataId { get; private set; } - [InverseProperty(nameof(Database.Data.CalorimetricData.Resources))] - public CalorimetricData? CalorimetricData { get; set; } + public string FilePath => + Path.Combine(FilesDirectoryPath, FileName); - public Guid? HygrothermalDataId { get; private set; } + public string AbsoluteFilePath => + Path.GetFullPath(FilePath); - [InverseProperty(nameof(Database.Data.HygrothermalData.Resources))] - public HygrothermalData? HygrothermalData { get; set; } + public ICollection ArchivedFilesMetaInformation { get; private set; } = []; - public Guid? OpticalDataId { get; private set; } + // Note that at least one data ID is always present. So `Guid.Empty` will never be used. + [Projectable] + public Guid DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; - [InverseProperty(nameof(Database.Data.OpticalData.Resources))] - public OpticalData? OpticalData { get; set; } + public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; - public Guid? PhotovoltaicDataId { get; private set; } + [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 - [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] - public PhotovoltaicData? PhotovoltaicData { get; set; } + [Projectable] + public Guid? GetDataId(Database.Enumerations.DataKind dataKind) => + dataKind switch + { + 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(Database.Enumerations.DataKind dataKind) => + dataKind switch + { + 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}"), + }; - 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; } + public Guid? CalorimetricDataId { 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(CalorimetricData.Resources))] + public CalorimetricData? CalorimetricData { get; set; } - [InverseProperty(nameof(Parent))] - public ICollection Children { get; } = new List(); + public Guid? GeometricDataId { get; private 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 - ) + [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))] + public OpticalData? OpticalData { get; set; } + + public Guid? PhotovoltaicDataId { get; private set; } + + [InverseProperty(nameof(PhotovoltaicData.Resources))] + public PhotovoltaicData? PhotovoltaicData { get; set; } + + public Guid? ParentId { get; private set; } + + 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; } = []; + + public void UpdateFileExtension(string? fileExtension) + { + var oldFilePath = FilePath; + FileExtension = fileExtension; + File.Move( + oldFilePath, + FilePath, + overwrite: true + ); + } + + 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); + } + + 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(); + } + + internal void UpdateRoot( + string description, + Guid dataFormatId, + string? fileExtension, + ICollection archivedFilesMetaInformation + ) + { + if (ParentId is not null) { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; + throw new InvalidOperationException($"This resource with ID {Id} is not a root but the child of {ParentId}."); } - - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? parentId, - ICollection archivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethod? appliedConversionMethod - ) - : this( - description: description, - hashValue: hashValue, - dataFormatId: dataFormatId, - parentId: parentId - ) + Description = description; + DataFormatId = dataFormatId; + if (fileExtension != FileExtension) { - ArchivedFilesMetaInformation = archivedFilesMetaInformation; - AppliedConversionMethod = appliedConversionMethod; + UpdateFileExtension(fileExtension); } + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + } - // `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 - ) + internal void UpdateChild( + string description, + Guid dataFormatId, + string? fileExtension, + ICollection archivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethod appliedConversionMethod + ) + { + if (ParentId is null) { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; + 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; + } - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? parentId - ) + internal void SetParent( + Guid parentId, + ToTreeVertexAppliedConversionMethod appliedConversionMethod + ) + { + if (ParentId is null) { - Description = description; - HashValue = hashValue; - DataFormatId = dataFormatId; - ParentId = parentId; + 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/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 97501231..8ac18146 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,60 +1,93 @@ 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 -{ - public sealed class HygrothermalData +namespace Database.Data; + +public sealed class HygrothermalData : DataX +{ + public const string TableName = "hygrothermal_data"; + + public static readonly DataKind TheKind = DataKind.HYGROTHERMAL_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + public HygrothermalData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + AppliedMethod appliedMethod + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) + { + } + + // `DbContext` needs this constructor without owned entities. + public HygrothermalData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + } + + [NotMapped] + public override DataKind Kind => TheKind; + + [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 + ) { - [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] - public override ICollection Resources { get; } = new List(); - - 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 - ) - { - } - - // `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 - ) - { - } + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 5d623eb5..4064e5b0 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,15 +1,27 @@ using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Database.GraphQl; +using Database.GraphQl.Scalars; +using HotChocolate; using HotChocolate.Types; +using NodaTime; -namespace Database.Data +namespace Database.Data; + +[InterfaceType("Approval")] +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.TypeDiscriminatorPropertyName)] +[JsonDerivedType(typeof(ResponseApproval), typeDiscriminator: nameof(ResponseApproval))] +[JsonDerivedType(typeof(DataApproval), typeDiscriminator: nameof(DataApproval))] +public interface IApproval { - [InterfaceType("Approval")] - public interface IApproval - { - DateTime Timestamp { get; } - string Signature { get; } - string KeyFingerprint { get; } - string Query { get; } - string Response { get; } - } -} + OffsetDateTime Timestamp { get; } + string Signature { get; } + string KeyFingerprint { get; } + + [GraphQLType>] 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/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 93c6a08b..a343394c 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,20 +1,58 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.GraphQl; -namespace Database.Data +namespace Database.Data; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.TypeDiscriminatorPropertyName)] +[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, IAuditable { - 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; } - } -} + public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); + + Guid? UserId { get; } + Guid ComponentId { get; } + string? Name { get; } + string? Description { get; } + string[] Warnings { get; } + Guid CreatorId { get; } + AppliedMethod AppliedMethod { get; } + ICollection Approvals { get; } + ICollection Resources { get; } + ResponseApproval? Approval { get; set; } + string Locale { get; } + DataAccessPolicy? AccessPolicy { get; set; } + PublishingState PublishingState { get; } + + [NotMapped] + DataKind Kind { get; } + + void Update( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + DateTimeOffset createdAt, + Guid creatorId + ); + + void Publish(); + + void Retract(); + + Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ); +} \ No newline at end of file diff --git a/backend/src/Data/IEntity.cs b/backend/src/Data/IEntity.cs index 8fcca168..4e86955f 100644 --- a/backend/src/Data/IEntity.cs +++ b/backend/src/Data/IEntity.cs @@ -1,11 +1,10 @@ -using Guid = System.Guid; +using System; -namespace Database.Data +namespace Database.Data; + +public interface IEntity { - public interface IEntity - { - Guid Id { get; } + 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/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/IReference.cs b/backend/src/Data/IReference.cs new file mode 100644 index 00000000..c8e797f0 --- /dev/null +++ b/backend/src/Data/IReference.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; +using Database.GraphQl; + +namespace Database.Data; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.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/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs new file mode 100644 index 00000000..38cbc903 --- /dev/null +++ b/backend/src/Data/LifeCycleData.cs @@ -0,0 +1,93 @@ +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; + +public sealed class LifeCycleData + : DataX +{ + public const string TableName = "lifeCycle_data"; + + public static readonly DataKind TheKind = DataKind.LIFE_CYCLE_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + public LifeCycleData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset 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, + DateTimeOffset createdAt + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + } + + [NotMapped] + public override DataKind Kind => TheKind; + + [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 + ) + { + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/backend/src/Data/NamedMethodArgument.cs b/backend/src/Data/NamedMethodArgument.cs index 13bc8f83..f05970ab 100644 --- a/backend/src/Data/NamedMethodArgument.cs +++ b/backend/src/Data/NamedMethodArgument.cs @@ -1,25 +1,14 @@ -using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class NamedMethodArgument - : IDisposable - { - public string Name { get; private set; } - public JsonDocument Value { get; private set; } - - public NamedMethodArgument( - string name, - JsonDocument value - ) - { - Name = name; - Value = value; - } +namespace Database.Data; - public void Dispose() => Value.Dispose(); - } +[Owned] +public sealed class NamedMethodArgument( + string name, + JsonElement value + ) +{ + public string Name { get; private set; } = name; + public JsonElement Value { get; private set; } = value; } \ No newline at end of file diff --git a/backend/src/Data/NamedMethodSource.cs b/backend/src/Data/NamedMethodSource.cs index 1245a4ec..72836cd3 100644 --- a/backend/src/Data/NamedMethodSource.cs +++ b/backend/src/Data/NamedMethodSource.cs @@ -1,29 +1,22 @@ using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class NamedMethodSource( + string name + ) { - [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; - } - - // `DbContext` needs this constructor without owned entities. - public NamedMethodSource( - string name - ) - { - Name = name; - } + Name = name; + Value = value; } + + 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 new file mode 100644 index 00000000..973dfe86 --- /dev/null +++ b/backend/src/Data/Numeration.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +[Owned] +public sealed class Numeration( + string? prefix, + string mainNumber, + string? suffix + ) +{ + [MinLength(1)] public string? Prefix { get; private set; } = prefix; + + [Required][MinLength(1)] public string MainNumber { get; private set; } = mainNumber; + + [MinLength(1)] public string? Suffix { get; private set; } = suffix; +} \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 477c7d95..eef0e132 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,94 +1,185 @@ 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; -namespace Database.Data -{ - public sealed class OpticalData +namespace Database.Data; + +public sealed class OpticalData : DataX +{ + public const string TableName = "optical_data"; + + public static readonly DataKind TheKind = DataKind.OPTICAL_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + + public OpticalData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + OpticalComponentType? type, + OpticalComponentSubtype? subtype, + CoatedSide coatedSide, + AppliedMethod appliedMethod, + double[] nearnormalHemisphericalVisibleTransmittances, + double[] nearnormalHemisphericalVisibleReflectances, + double[] nearnormalHemisphericalSolarTransmittances, + double[] nearnormalHemisphericalSolarReflectances, + double[] infraredEmittances, + double[] colorRenderingIndices, + ICollection cielabColors + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) + { + Type = type; + Subtype = subtype; + CoatedSide = coatedSide; + NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; + NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; + NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; + NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; + InfraredEmittances = infraredEmittances; + ColorRenderingIndices = colorRenderingIndices; + CielabColors = cielabColors; + } + + // `DbContext` needs this constructor without owned entities. + public OpticalData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + OpticalComponentType? type, + OpticalComponentSubtype? subtype, + CoatedSide coatedSide, + double[] nearnormalHemisphericalVisibleTransmittances, + double[] nearnormalHemisphericalVisibleReflectances, + double[] nearnormalHemisphericalSolarTransmittances, + double[] nearnormalHemisphericalSolarReflectances, + double[] infraredEmittances, + double[] colorRenderingIndices + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) { - [InverseProperty(nameof(GetHttpsResource.OpticalData))] - public override ICollection Resources { get; } = new List(); + Type = type; + Subtype = subtype; + CoatedSide = coatedSide; + 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(); + [NotMapped] + public override DataKind Kind => TheKind; - 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; } = []; + + [InverseProperty(nameof(DataAccessPolicy.OpticalData))] + public override DataAccessPolicy? AccessPolicy { get; set; } - // `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 - ) + 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; } + public double[] NearnormalHemisphericalSolarReflectances { get; private set; } + public double[] InfraredEmittances { get; private set; } + public double[] ColorRenderingIndices { get; private set; } + public ICollection CielabColors { get; private set; } = []; + + public override async Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + if (dataFormatId == IData.BedJsonDataFormatId) { - NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; - NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; - NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; - NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; - InfraredEmittances = infraredEmittances; - ColorRenderingIndices = colorRenderingIndices; + NearnormalHemisphericalVisibleTransmittances = + await new DoubleResultsOpticalDataJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.VISIBLE, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.TRANSMITTANCE + ).Extract(filePath); + NearnormalHemisphericalVisibleReflectances = + await new DoubleResultsOpticalDataJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.VISIBLE, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.REFLECTANCE + ).Extract(filePath); + NearnormalHemisphericalSolarTransmittances = + await new DoubleResultsOpticalDataJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.SOLAR, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.TRANSMITTANCE + ).Extract(filePath); + NearnormalHemisphericalSolarReflectances = + await new DoubleResultsOpticalDataJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.SOLAR, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.REFLECTANCE + ).Extract(filePath); + InfraredEmittances = + await new DoubleResultsOpticalDataJsonExtractor( + 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/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 06d37513..fb987981 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,60 +1,93 @@ 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 -{ - public sealed class PhotovoltaicData +namespace Database.Data; + +public sealed class PhotovoltaicData : DataX +{ + public const string TableName = "photovoltaic_data"; + + public static readonly DataKind TheKind = DataKind.PHOTOVOLTAIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + + public PhotovoltaicData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt, + AppliedMethod appliedMethod + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) + { + } + + // `DbContext` needs this constructor without owned entities. + public PhotovoltaicData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + } + + [NotMapped] + public override DataKind Kind => TheKind; + + [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 + ) { - [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] - public override ICollection Resources { get; } = new List(); - - 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 - ) - { - } - - // `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 - ) - { - } + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; } -} +} \ 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..611efcb4 --- /dev/null +++ b/backend/src/Data/Publication.cs @@ -0,0 +1,66 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Database.GraphQl.Scalars; +using HotChocolate; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +[Owned] +public sealed class Publication( + string? title, + string? @abstract, + string? section, + string[]? authors, + string? doi, + string? arXiv, + string? urn, + Uri? webAddress + ) + : 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.")] + [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.")] + [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.")] + [GraphQLType] + public string? Urn { get; private set; } = urn; + + [Url] + [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; + + [MinLength(1)] public string? Abstract { get; private set; } = @abstract; + + [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/Data/Reference.cs b/backend/src/Data/Reference.cs new file mode 100644 index 00000000..065baac0 --- /dev/null +++ b/backend/src/Data/Reference.cs @@ -0,0 +1,49 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; +using Database.Json; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +[Owned] +[JsonConverter(typeof(ReferenceJsonConverter))] +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/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index d2e888f5..7e002f80 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,30 +1,33 @@ -// using System; +using System; +using System.Text.Json; +using Database.GraphQl.Scalars; +using HotChocolate; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using NodaTime; -// 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; -// } +namespace Database.Data; -// 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; } -// } -// } +[Owned] +public sealed class ResponseApproval( + OffsetDateTime timestamp, + string signature, + string keyFingerprint, + string query, + 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; +} \ 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..b6aa5a2a --- /dev/null +++ b/backend/src/Data/Standard.cs @@ -0,0 +1,41 @@ +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( + string? title, + string? @abstract, + string? section, + int? year, + Standardizer[] standardizers, + Uri? 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; } = 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; } = standardizers; + + [Url] public Uri? Locator { get; private set; } = locator; + + [MinLength(1)] public string? Title { get; private set; } = title; + + [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; } = section; +} \ No newline at end of file diff --git a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs index 6d9369e4..076b1f69 100644 --- a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs +++ b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs @@ -2,36 +2,28 @@ 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( + Guid methodId, + string sourceName + ) +{ + public ToTreeVertexAppliedConversionMethod( + Guid methodId, + ICollection arguments, + string sourceName + ) : this( - methodId: methodId, - sourceName: sourceName + methodId, + sourceName ) - { - Arguments = arguments; - } - - // `DbContext` needs this constructor without owned entities. - public ToTreeVertexAppliedConversionMethod( - Guid methodId, - string sourceName - ) - { - MethodId = methodId; - SourceName = sourceName; - } + { + Arguments = arguments; } -} + + public Guid MethodId { get; private set; } = methodId; + public ICollection Arguments { 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 new file mode 100644 index 00000000..858e7a98 --- /dev/null +++ b/backend/src/Data/User.cs @@ -0,0 +1,18 @@ +namespace Database.Data; + +public sealed class User( + string subject, + string name +) +: AuditableEntity +{ + public string Subject { get; private set; } = subject; + public string Name { get; private set; } = name; + + public void Update( + string name + ) + { + Name = name; + } +} \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 0d23ecf9..d331c23b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,13 +1,11 @@ + + - net5.0 - 9.0 31666837686487542159 + true - enable - true - true @@ -15,40 +13,88 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + false + + **/obj/**;**/bin/**;**/obj%5CDebug/**;**/bin%5CDebug/**;**/logs/**;**/files/** + + + + + - + + + PreserveNewest + + + PreserveNewest + + PreserveNewest + + + + + + 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/CoatedSide.cs b/backend/src/Enumerations/CoatedSide.cs new file mode 100644 index 00000000..e9ebe5d6 --- /dev/null +++ b/backend/src/Enumerations/CoatedSide.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum CoatedSide +{ + PRIME, + NON_PRIME, + BOTH, + NEITHER, + UNKNOWN, + NOT_APPLICABLE +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataKind.cs b/backend/src/Enumerations/DataKind.cs index 27a343e6..840c5668 100644 --- a/backend/src/Enumerations/DataKind.cs +++ b/backend/src/Enumerations/DataKind.cs @@ -1,13 +1,14 @@ -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, + GEOMETRIC_DATA, + HYGROTHERMAL_DATA, + LIFE_CYCLE_DATA, + OPTICAL_DATA, + PHOTOVOLTAIC_DATA +} \ No newline at end of file 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..4b5237dc --- /dev/null +++ b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs @@ -0,0 +1,55 @@ +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 + { + IncidenceDirection.NEARNORMAL => ".polar <= 10", + IncidenceDirection.HEMISPHERICAL => "=='hemispherical'", + _ => throw new ArgumentOutOfRangeException(nameof(incidenceDirection), $"Unsupported incidence direction {incidenceDirection}."), + }; + } + + [Pure] + 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}."), + }; + } + + [Pure] + 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}."), + }; + } + + [Pure] + public static string ToJsonProperty(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/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/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/Enumerations/OpticalComponentSubtype.cs b/backend/src/Enumerations/OpticalComponentSubtype.cs new file mode 100644 index 00000000..fbeb869e --- /dev/null +++ b/backend/src/Enumerations/OpticalComponentSubtype.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum OpticalComponentSubtype +{ + 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/Enumerations/OpticalComponentType.cs b/backend/src/Enumerations/OpticalComponentType.cs new file mode 100644 index 00000000..0f43fd03 --- /dev/null +++ b/backend/src/Enumerations/OpticalComponentType.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum OpticalComponentType +{ + GLAZING, + SHADING +} \ 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/Enumerations/Standardizer.cs b/backend/src/Enumerations/Standardizer.cs new file mode 100644 index 00000000..02affdd5 --- /dev/null +++ b/backend/src/Enumerations/Standardizer.cs @@ -0,0 +1,29 @@ +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/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/Extensions/HttpContextExtensions.cs b/backend/src/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..585e10ba --- /dev/null +++ b/backend/src/Extensions/HttpContextExtensions.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics.Contracts; +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}"; + + [Pure] + public static string? ExtractBearerToken(this HttpContext httpContext) + { + var bearerTokenPrefix = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} "; + return httpContext.Request?.Headers?.Authorization + .FirstOrDefault( + _ => _ is not null + && _.TrimStart().StartsWith(bearerTokenPrefix, StringComparison.Ordinal) + ) + ?.TrimStart() + ?[bearerTokenPrefix.Length..] + ?.TrimEnd(); + } +} \ 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 new file mode 100644 index 00000000..0fb6d107 --- /dev/null +++ b/backend/src/Extensions/NodaTimeExtensions.cs @@ -0,0 +1,34 @@ +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); + } + + 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; + } + } +} \ No newline at end of file diff --git a/backend/src/Extensions/ResultExtensions.cs b/backend/src/Extensions/ResultExtensions.cs new file mode 100644 index 00000000..216a54d9 --- /dev/null +++ b/backend/src/Extensions/ResultExtensions.cs @@ -0,0 +1,96 @@ +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, + [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!"); + } + } + + [Pure] + 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!"); + } + } + + [Pure] + 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!"); + } + } + + [Pure] + 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/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 7acfb70c..9fa2d2d1 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -1,28 +1,48 @@ -namespace Database.Extensions +using System; +using System.Text; +using System.Diagnostics.Contracts; + +namespace Database.Extensions; + +public static class StringExtensions { - public static class StringExtensions + [Pure] + public static string FirstCharToLower(this string value) { - 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(value) + || !char.IsLetter(value, 0) + || char.IsLower(value, 0) + ? value + : char.ToLowerInvariant(value[0]) + value[1..]; + } - public static string Base64Encode(this string plainText) - { - return System.Convert.ToBase64String( - System.Text.Encoding.UTF8.GetBytes(plainText) - ); - } + [Pure] + public static string? NullIfEmpty(this string value) + => string.IsNullOrEmpty(value) ? null : value; - public static string Base64Decode(this string base64EncodedData) - { - return System.Text.Encoding.UTF8.GetString( - System.Convert.FromBase64String(base64EncodedData) - ); - } + [Pure] + public static string? NullIfWhitespace(this string value) + => string.IsNullOrWhiteSpace(value) ? null : value; + + [Pure] + public static string Base64Encode(this string plainText) + { + return Convert.ToBase64String( + Encoding.UTF8.GetBytes(plainText) + ); + } + + [Pure] + public static string Base64Decode(this string base64EncodedData) + { + return Encoding.UTF8.GetString( + Convert.FromBase64String(base64EncodedData) + ); + } + + [Pure] + public static string Enquote(this string str) + { + return "\"" + str + "\""; } } \ 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..12fec5ba --- /dev/null +++ b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using Database.Data; +using Database.Enumerations.DataPoints; +using Database.Json; +using Json.Path; + +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..bdd36446 --- /dev/null +++ b/backend/src/Extractors/ClassJsonPathExtractor.cs @@ -0,0 +1,35 @@ +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; + +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..5dfc3844 --- /dev/null +++ b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs @@ -0,0 +1,16 @@ +using Database.Enumerations.DataPoints; +using Json.Path; + +namespace Database.Extractors; + +public sealed class ColorRenderingIndexResultsJsonExtractor( +) : StructJsonPathExtractor( + JsonPath.Parse( + $"$.data[*].dataPoints[?(" + + $"@.incidence.wavelengths.integral=='{WavelengthsIntegral.VISIBLE.ToJsonEnum()}'" + + $")].results.colorTransmittance.colorRenderingIndices[*]['ra','raOutIn']" + ), + ExtractNumberWithUncertainty +) +{ +} \ No newline at end of file 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/DoubleResultsOpticalDataJsonExtractor.cs b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs new file mode 100644 index 00000000..f6b33220 --- /dev/null +++ b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs @@ -0,0 +1,22 @@ +using Database.Enumerations.DataPoints; +using Json.Path; + +namespace Database.Extractors; + +public sealed class DoubleResultsOpticalDataJsonExtractor( + 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.ToJsonProperty()}" + ), + ExtractNumberWithUncertainty +) +{ +} \ No newline at end of file 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/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..2dfbd086 --- /dev/null +++ b/backend/src/Extractors/StructJsonPathExtractor.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using Database.Extensions; +using Json.More; +using Json.Path; + +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(); + } + + 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/Filters/DisableFormValueModelBindingAttribute.cs b/backend/src/Filters/DisableFormValueModelBindingAttribute.cs index b38ecae8..9da0d1c8 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 sealed 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..e8f5ceed 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 sealed 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/AccessPolicies/AccessPolicyDataLoaders.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs new file mode 100644 index 00000000..667a05cc --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs @@ -0,0 +1,155 @@ +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 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, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return await databaseContext.DataAccessPolicies + .AsNoTrackingWithIdentityResolution() + .Where(_ => dataIds.Contains(_.DataId ?? Guid.Empty)) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToDictionaryAsync(_ => _.DataId ?? Guid.Empty, cancellationToken); + } + + [DataLoader] + 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 + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.InstitutionAccessPolicies, + _ => _.DataAccessPolicyId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetOpenIdConnectApplicationAccessPoliciesByDataAccessPolicyIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken 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 new file mode 100644 index 00000000..70967925 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -0,0 +1,136 @@ +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; + +namespace Database.GraphQl.AccessPolicies; + +[ExtendObjectType(nameof(Query))] +public sealed class AccessPolicyQueries +{ + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task GetDataAccessPolicyAsync( + Guid? dataId, + ApplicationDbContext databaseContext, + IDataAccessPolicyByDataIdDataLoader byId, + CommonAuthorization authorization, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return null; + } + if (dataId is null) + { + return await databaseContext.DataAccessPolicies.AsNoTracking() + .Where(_ => _.DataId == null) + .SingleAsync(cancellationToken); + } + return await byId.LoadAsync(dataId ?? Guid.Empty, 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/AccessPolicyTypeBase.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs new file mode 100644 index 00000000..b49bc369 --- /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(_ => _.IsWithinAccessLimitInTimeSpan).Ignore(); + // descriptor.Field(_ => _.IsWithinTimeSpan).Ignore(); + } +} \ 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..7ae8363a --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.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 HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record ClearInstitutionAccessPoliciesInput +( + DataReferenceInput? Data +); + +[SuppressMessage("Naming", "CA1707")] +public enum ClearInstitutionAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ClearInstitutionAccessPoliciesError( + ClearInstitutionAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearInstitutionAccessPoliciesPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearInstitutionAccessPoliciesMutation +: DataMutationBase +{ + protected override ClearInstitutionAccessPoliciesPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearInstitutionAccessPoliciesError NewError( + ClearInstitutionAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task ClearInstitutionAccessPoliciesAsync( + ClearInstitutionAccessPoliciesInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearInstitutionAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearInstitutionAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .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 new file mode 100644 index 00000000..411da690 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.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 HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record ClearOpenIdConnectApplicationAccessPoliciesInput +( + DataReferenceInput? Data +); + +[SuppressMessage("Naming", "CA1707")] +public enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ClearOpenIdConnectApplicationAccessPoliciesError( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearOpenIdConnectApplicationAccessPoliciesPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearOpenIdConnectApplicationAccessPoliciesMutation +: DataMutationBase +{ + protected override ClearOpenIdConnectApplicationAccessPoliciesPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearOpenIdConnectApplicationAccessPoliciesError NewError( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task ClearOpenIdConnectApplicationAccessPoliciesAsync( + ClearOpenIdConnectApplicationAccessPoliciesInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearOpenIdConnectApplicationAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .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 new file mode 100644 index 00000000..7d19f425 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.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 HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record ClearUserAccessPoliciesInput +( + DataReferenceInput? Data +); + +[SuppressMessage("Naming", "CA1707")] +public enum ClearUserAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ClearUserAccessPoliciesError( + ClearUserAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearUserAccessPoliciesPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearUserAccessPoliciesMutation +: DataMutationBase +{ + protected override ClearUserAccessPoliciesPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearUserAccessPoliciesError NewError( + ClearUserAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task ClearUserAccessPoliciesAsync( + ClearUserAccessPoliciesInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearUserAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearUserAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .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..f15ffe9a --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs @@ -0,0 +1,107 @@ +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.Authorization; +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] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs new file mode 100644 index 00000000..ee189f03 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.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.Enumerations; +using Database.Extensions; +using HotChocolate.Types; +using HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs new file mode 100644 index 00000000..0c5ebb03 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -0,0 +1,37 @@ +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); + // 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(_ => _.IsEveryoneAllowed); + descriptor.Field(_ => _.Combinator); + descriptor.Field(_ => _.UserAccessPolicies); + descriptor.Field(_ => _.InstitutionAccessPolicies); + descriptor.Field(_ => _.OpenIdConnectApplicationAccessPolicies); + descriptor.Field(_ => _.DataId); + descriptor.Field(_ => _.DataKind); + // 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/AccessPolicies/DataAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs new file mode 100644 index 00000000..8c5c814b --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs @@ -0,0 +1,19 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class DataAccessPolicySortType + : AuditableEntitySortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + 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 new file mode 100644 index 00000000..95ff4b63 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -0,0 +1,267 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.GraphQl.DataX; +using Database.GraphQl.Extensions; +using Database.GraphQl.Entities; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class DataAccessPolicyType + : EntityType +{ + protected override void Configure( + 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) + .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!)); + descriptor + .Field(_ => _.IsNobodyAllowed) + .Cost(0) + .ResolveWith(t => + Resolvers.IsNobodyAllowedAsync(default!, default!, default!, default!)); + descriptor + .Field(_ => _.IsEveryoneAllowed) + .Cost(0) + .ResolveWith(t => + Resolvers.IsEveryoneAllowedAsync(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 + { + 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 + ); + } + + 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 IsEveryoneAllowedAsync( + [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; + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public static async Task> GetUserAccessPoliciesAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + 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( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + 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( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + 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 new file mode 100644 index 00000000..eb7121ac --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs @@ -0,0 +1,28 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicyFilterType + : AuditableEntityFilterType +{ + protected override void Configure( + 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); + descriptor.Field(_ => _.IsWithinTimeSpan); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); + } +} \ 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..28308513 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs @@ -0,0 +1,18 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicySortType + : AuditableEntitySortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(InstitutionAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.InstitutionId); + } +} \ 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..deb645be --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GreenDonut; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(_ => _.DataAccessPolicy) + .Type>>() + .ResolveWith(_ => Resolvers.GetDataAccessPolicyAsync(default!, default!)); + descriptor + .Field(nameof(InstitutionAccessPolicy.InstitutionId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetInstitutionAsync(default!, default!)); + } + + 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 + ) + { + return byId.LoadAsync(parent.InstitutionId); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs new file mode 100644 index 00000000..249a01f5 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs @@ -0,0 +1,28 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicyFilterType + : AuditableEntityFilterType +{ + protected override void Configure( + 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); + descriptor.Field(_ => _.IsWithinTimeSpan); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs new file mode 100644 index 00000000..ee36eff1 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs @@ -0,0 +1,18 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicySortType + : AuditableEntitySortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.ClientId); + } +} \ 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..7dff3fb0 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GreenDonut; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + 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>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetOpenIdConnectApplicationAsync(default!, default!)); + } + + 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 + ) + { + return byClientId.LoadAsync(parent.ClientId); + } + } +} \ 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..d651be14 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs @@ -0,0 +1,103 @@ +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.Authorization; +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] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs new file mode 100644 index 00000000..8cd7c22a --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs @@ -0,0 +1,101 @@ +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.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .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 new file mode 100644 index 00000000..471b67d2 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.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 HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; +using GraphQL.Client.Abstractions.Utilities; + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync(_ => _.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs new file mode 100644 index 00000000..d055d66e --- /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 HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync(_ => _.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs new file mode 100644 index 00000000..e34c567f --- /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 HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync(_ => _.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs new file mode 100644 index 00000000..fa46aa1b --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs @@ -0,0 +1,119 @@ +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 HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var institutionAccessPolicy = await context.InstitutionAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs new file mode 100644 index 00000000..4fdf0a4d --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -0,0 +1,120 @@ +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 HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var applicationAccessPolicy = await context.OpenIdConnectApplicationAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs new file mode 100644 index 00000000..f387a52b --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs @@ -0,0 +1,121 @@ +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 HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 dataId = input.Data?.DataId; + var userAccessPolicy = await context.UserAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == 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); + } +} diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs new file mode 100644 index 00000000..58cc2fdf --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs @@ -0,0 +1,28 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicyFilterType + : AuditableEntityFilterType +{ + protected override void Configure( + 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); + descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); + descriptor.Field(_ => _.IsWithinTimeSpan); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); + } +} \ 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..dffb57fe --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs @@ -0,0 +1,18 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicySortType + : AuditableEntitySortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(UserAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.UserId); + } +} \ 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..b76e42cb --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GreenDonut; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(_ => _.DataAccessPolicy) + .Type>>() + .ResolveWith(_ => Resolvers.GetDataAccessPolicyAsync(default!, default!)); + descriptor + .Field(nameof(UserAccessPolicy.UserId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetUserAsync(default!, default!)); + } + + 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 + ) + { + return byId.LoadAsync(parent.UserId); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AppliedMethodInput.cs b/backend/src/GraphQl/AppliedMethodInput.cs index 2104e04a..54a548fe 100644 --- a/backend/src/GraphQl/AppliedMethodInput.cs +++ b/backend/src/GraphQl/AppliedMethodInput.cs @@ -1,11 +1,26 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed record AppliedMethodInput( + Guid MethodId, + IReadOnlyList Arguments, + IReadOnlyList Sources +) { - public 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/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/Associations/AuditableAssociationFilterType.cs b/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs new file mode 100644 index 00000000..c03718ca --- /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(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + } +} diff --git a/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs b/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs new file mode 100644 index 00000000..f6c4ac73 --- /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(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + } +} 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 4d93156a..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GreenDonut; -using Database.GraphQl.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.CalorimetricDataX -{ - public sealed class CalorimetricDataByIdDataLoader - : EntityByIdDataLoader - { - public CalorimetricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.CalorimetricData - ) - { - } - } -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index f6b5f098..98fe7809 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -1,19 +1,39 @@ +using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; 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.Name(nameof(CalorimetricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.GValues); + descriptor.Field(_ => _.UValues); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); } -} +} \ 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/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs deleted file mode 100644 index b4ef2d4e..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Extensions; -using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; -using HotChocolate.Types; - -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( - CreateCalorimetricDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - [ScopedService] 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, - // 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(), - 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 - ) - ).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); - } - } -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs deleted file mode 100644 index 45ce9d33..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.CalorimetricDataX -{ - public abstract class CalorimetricDataPayload - : Payload - where TCalorimetricDataError : IUserError - { - public Data.CalorimetricData? CalorimetricData { get; } - public IReadOnlyCollection? Errors { get; } - - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData - ) - { - CalorimetricData = calorimetricData; - } - - protected CalorimetricDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected CalorimetricDataPayload( - TCalorimetricDataError error - ) - : this(new[] { error }) - { - } - - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData, - IReadOnlyCollection errors - ) - { - CalorimetricData = calorimetricData; - Errors = errors; - } - - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData, - TCalorimetricDataError error - ) - : this( - calorimetricData, - new[] { error } - ) - { - } - } -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 6c5be9e4..d1337e06 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -1,45 +1,107 @@ using System; -using System.Linq; 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.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class CalorimetricDataQueries +: DataQueriesBase { - [ExtendObjectType(nameof(Query))] - public sealed class CalorimetricDataQueries + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllCalorimetricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) { - [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] - [UseSorting] - public IQueryable GetAllCalorimetricData( - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.CalorimetricData; - } + return GetAllDataAsync( + databaseContext => databaseContext.CalorimetricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingCalorimetricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.CalorimetricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } - public Task GetCalorimetricDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - CalorimetricDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + [UseFiltering] + public Task HasCalorimetricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.CalorimetricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetCalorimetricDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.CalorimetricData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs deleted file mode 100644 index bc3fd6b8..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.CalorimetricDataX -{ - public sealed class CalorimetricDataResolvers - { - public Task GetGetHttpsResources( - [Parent] Data.CalorimetricData calorimetricData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(calorimetricData.Id, cancellationToken); - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.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/CalorimetricDataSortType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs new file mode 100644 index 00000000..e1136a82 --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs index a8181c73..f395b413 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs @@ -1,28 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; using HotChocolate.Types; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataType + : DataTypeBase { - 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); } -} +} \ 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 ffef708a..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.CalorimetricDataX -{ - public sealed class CreateCalorimetricDataError - : GraphQl.UserErrorBase - { - public CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } - } -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs deleted file mode 100644 index 489d1bdb..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Database.GraphQl.CalorimetricDataX -{ - public enum CreateCalorimetricDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs deleted file mode 100644 index d155ce75..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Types; - -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, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource, - double[] GValues, - double[] UValues - ); -} diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs new file mode 100644 index 00000000..2c2eeb8a --- /dev/null +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -0,0 +1,177 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.CalorimetricDataX; + +public sealed record CreateCalorimetricDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, + double[] GValues, + double[] UValues +) : IValidateCreateInput +{ + public CalorimetricData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new CalorimetricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + GValues, + UValues + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public 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 +} + +public sealed record CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateCalorimetricDataPayload( + CalorimetricData? CalorimetricData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreateCalorimetricDataMutation +: CreateDataMutationBase +{ + // [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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateCalorimetricDataAsync( + CreateCalorimetricDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateCalorimetricDataErrorCode.UNAUTHENTICATED, + CreateCalorimetricDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreateCalorimetricDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_DATABASE, + CreateCalorimetricDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var calorimetricData = input.ToDomainModel( + currentUserOrInstitution.CurrentUser?.Uuid, + dataFormat.Extension + ); + context.CalorimetricData.Add(calorimetricData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + calorimetricData, + CreateCalorimetricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(calorimetricData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(calorimetricData, null); + } +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs deleted file mode 100644 index eaf6ae61..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Database.GraphQl.CalorimetricDataX -{ - public sealed class CreateCalorimetricDataPayload - : CalorimetricDataPayload - { - public CreateCalorimetricDataPayload( - Data.CalorimetricData calorimetricData - ) - : base(calorimetricData) - { - } - - public CreateCalorimetricDataPayload( - CreateCalorimetricDataError error - ) - : base(error) - { - } - } -} diff --git a/backend/src/GraphQl/CielabColorInput.cs b/backend/src/GraphQl/CielabColorInput.cs index b4e05998..2425cc83 100644 --- a/backend/src/GraphQl/CielabColorInput.cs +++ b/backend/src/GraphQl/CielabColorInput.cs @@ -1,8 +1,24 @@ -namespace Database.GraphQl +using Database.Data; +using Database.Enumerations; + +namespace Database.GraphQl; + +public sealed record CielabColorInput( + double LStar, + double AStar, + double BStar, + CalorimetricObserver? Observer, + Illuminant? Illuminant +) { - public record CielabColorInput( - double LStar, - double AStar, - double BStar - ); -} \ 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/Common/OpenEndedDateTimeRangeInput.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs index c7b0e746..a7015e0d 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs @@ -1,9 +1,8 @@ -using DateTime = System.DateTime; +using NodaTime; -namespace Database.GraphQl.Common -{ - public record OpenEndedDateTimeRangeInput( - DateTime? From, - DateTime? Until - ); -} \ No newline at end of file +namespace Database.GraphQl.Common; + +public sealed record OpenEndedDateTimeRangeInput( + 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 5a3835b0..20108396 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -1,44 +1,40 @@ using HotChocolate.Types; +using NodaTime; using NpgsqlTypes; -using DateTime = System.DateTime; +using DateTimeType = HotChocolate.Types.NodaTime.DateTimeType; -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(); - - 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("until") - .Type() - .Resolve(context => - { - var range = context.Parent>(); - return range.UpperBoundInfinite - ? null - : range.UpperBound; - } - ); - } + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(OpenEndedDateTimeRangeType)[..^"Type".Length]); + 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; + } + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index e7bd4848..9b73bbaa 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -1,39 +1,49 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using GreenDonut; -using HotChocolate; +using GreenDonut.Data; +using HotChocolate.CostAnalysis.Types; +using Database.Data; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class Connection( + TSubject subject, + Func createEdge, + QueryContext queryContext +) + where TSubject : IEntity + where TAssociationsByOneIdDataLoader : IDataLoader { - public abstract class Connection - where TSubject : Data.IEntity - where TAssociationsByAssociateIdDataLoader : IDataLoader - { - protected TSubject Subject { get; } - private readonly Func _createEdge; + protected TSubject Subject { get; } = subject; - protected Connection( - TSubject subject, - Func createEdge - ) - { - Subject = subject; - _createEdge = createEdge; - } + [Cost(0)] + public async Task GetTotalCountAsync( + TAssociationsByOneIdDataLoader dataLoader, + CancellationToken cancellationToken + ) + { + return ( + await dataLoader + .With(queryContext) + .LoadAsync(Subject.Id, cancellationToken) + ) + ?.Length ?? 0; + } - public async Task> GetEdgesAsync( - [DataLoader] TAssociationsByAssociateIdDataLoader dataLoader, - CancellationToken cancellationToken - ) + [Cost(0)] + public async IAsyncEnumerable GetEdgesAsync( + TAssociationsByOneIdDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + foreach (var association in await dataLoader.With(queryContext).LoadAsync(Subject.Id, cancellationToken) ?? []) { - return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) - ) - .Select(_createEdge); + yield return createEdge(association); } } } \ No newline at end of file diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 22e47a27..59f47b16 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -1,12 +1,24 @@ using System; +using Database.Data; using Database.Enumerations; +using NodaTime; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed record CrossDatabaseDataReferenceInput( + Guid DataId, + OffsetDateTime DataTimestamp, + DataKind DataKind, + Guid DatabaseId +) { - public record CrossDatabaseDataReferenceInput( - Guid DataId, - 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/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/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs deleted file mode 100644 index 4d6f8a92..00000000 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Database.GraphQl -{ - public 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/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs new file mode 100644 index 00000000..4dc6df76 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +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; +using Database.Extensions; +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; +using HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.Extensions.Logging; +using NodaTime; + +namespace Database.GraphQl.DataApprovals; + +public sealed record AddDataApprovalInput +( + Guid DataId, + DataKind DataKind, + OffsetDateTime Timestamp, + string Signature, + string KeyFingerprint, + string Query, + JsonElement Variables, + string Message, + Guid ApproverId, + ReferenceInput Statement +) : IIdentifyDataInput; + +[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 record AddDataApprovalPayload( + DataApproval? DataApproval, + IReadOnlyCollection? Errors +) : Payload; + +public static partial class Log +{ + [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 AddDataApprovalMutation +: DataMutationBase +{ + private sealed record Message( + JsonElement Statement, + 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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task AddDataApprovalAsync( + AddDataApprovalInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IsGnuPgFingerprintValid isGnuPgFingerprintValid, + ResponseApprovalService responseApprovalService, + SigningService signingService, + ApiRequestService apiRequestService, + ILogger logger, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + AddDataApprovalErrorCode.UNAUTHENTICATED, + AddDataApprovalErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var errors = new List(); + if (!await isGnuPgFingerprintValid.Do( + input.KeyFingerprint, + input.ApproverId, + input.Timestamp, + cancellationToken + )) + { + errors.Add( + 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()] + ) + ); + } + var sigantureVerificationResult = await signingService.VerifySignatureAsync(input.Message, input.Signature, input.KeyFingerprint, cancellationToken); + switch (sigantureVerificationResult) + { + case SigantureVerificationResult.FAILED_RECEIVING_KEY: + errors.Add( + NewError( + 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( + 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()] + ) + ); + break; + case SigantureVerificationResult.GOOD_SIGNATURE: + break; + default: + errors.Add( + 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()] + ) + ); + logger.UnknownSignatureVerificationResult(sigantureVerificationResult); + break; + } + ; + if (input.Statement.Standard is null + && input.Statement.Publication is null) + { + errors.Add( + NewError( + AddDataApprovalErrorCode.MISSING_STATEMENT, + "Both standard and publication are null.", + [nameof(input), nameof(input.Statement).ToLowerFirst()] + ) + ); + } + if (input.Statement.Standard is not null + && input.Statement.Publication is not null) + { + errors.Add( + NewError( + AddDataApprovalErrorCode.AMBIGUOUS_STATEMENT, + "Both standard and publication are non-null.", + [nameof(input), nameof(input.Statement).ToLowerFirst()] + ) + ); + } + 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( + 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()] + ) + ); + } + 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( + 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()] + ) + ); + } + } + 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( + 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()] + ) + ); + } + } + 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( + NewError( + 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( + 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()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + if ((await FetchDataAsync( + input, + AddDataApprovalErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + var approval = new DataApproval( + input.Timestamp, + input.Signature.Trim(), + input.KeyFingerprint, + input.Query.Trim(), + input.Variables, + input.Message, + input.ApproverId, + input.Statement.ToDomainModel() + ); + + data.Approvals.Add(approval); + await context.SaveChangesAsync(cancellationToken); + + 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 createResponseApprovalErrorPayload; + } + + return NewPayload(approval, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs new file mode 100644 index 00000000..ff19a04b --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs @@ -0,0 +1,41 @@ +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; + +public sealed class DataApprovalType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Statement) + .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/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs new file mode 100644 index 00000000..b3c0f598 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -0,0 +1,132 @@ +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 HotChocolate.Authorization; + +namespace Database.GraphQl.DataApprovals; + +public sealed record RemoveDataApprovalInput +( + Guid DataId, + DataKind DataKind, + string Signature +) : IIdentifyDataInput; + +[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 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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task RemoveDataApprovalAsync( + RemoveDataApprovalInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + RemoveDataApprovalErrorCode.UNAUTHENTICATED, + RemoveDataApprovalErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + RemoveDataApprovalErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + var approval = data.Approvals + .Where(a => a.Signature.Trim() == input.Signature.Trim()) + .SingleOrDefault(); + if (approval is null) + { + return NewPayload( + null, + [NewError( + RemoveDataApprovalErrorCode.UNKNOWN_APPROVAL, + $"Unknown data approval.", + [nameof(input), nameof(input.Signature).ToLowerFirst()] + )] + ); + } + + data.Approvals.Remove(approval); + await context.SaveChangesAsync(cancellationToken); + + 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 createResponseApprovalErrorPayload; + } + + return NewPayload(approval, null); + } +} diff --git a/backend/src/GraphQl/DataLoaders.cs b/backend/src/GraphQl/DataLoaders.cs new file mode 100644 index 00000000..13595ec4 --- /dev/null +++ b/backend/src/GraphQl/DataLoaders.cs @@ -0,0 +1,121 @@ +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 = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + 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 = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + 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 = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + 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 = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + 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 = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + 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/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs new file mode 100644 index 00000000..4b3b1349 --- /dev/null +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Enumerations; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using static Database.ApiRequests.DataFormatDataLoader; + +namespace Database.GraphQl; + +public interface IIdentifyDataInput +{ + public Guid DataId { get; } + public DataKind DataKind { get; } +} + +public abstract class DataMutationBase +: MutationBase +where TData : class +where TPayload : class +where TError : 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( + IData 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( + null, + [NewError( + creatingResponseApprovalFailedErrorCode, + $"Failed creating a response approval for the data set {data.Id} named '{data.Name}' with the error message: {exception.Message}", + [] + )] + ) + ); + } + 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]; + // First eagerly execute all `LoadAsync`s on data loaders to make it know all keys. + validateDataFormats[0] = dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); + foreach (var (index, archivedFileMetaInformation) in input.ArchivedFilesMetaInformation.Index()) + { + 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[0]; + if (dataFormat is null) + { + errors.Add( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [.. getHttpsResourcePath, nameof(input.DataFormatId).ToLowerFirst()] + ) + ); + } + foreach (var (index, archivedFileDataFormat) in dataFormats[1..].Index()) + { + 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 > 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/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/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs new file mode 100644 index 00000000..eb1c673f --- /dev/null +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using NodaTime; +using static Database.ApiRequests.DataFormatDataLoader; + +namespace Database.GraphQl.DataX; + +public interface IValidateCreateInput +{ + Guid ComponentId { get; } + DateTimeOffset 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( + IValidateCreateInput input, + TErrorCode illegalCreatedAtErrorCode, + IComponentByIdDataLoader componentByIdDataLoader, + TErrorCode unknownComponentErrorCode, + IInstitutionByIdDataLoader institutionByIdDataLoader, + TErrorCode unknownCreatorErrorCode, + IMethodByIdDataLoader methodByIdDataLoader, + TErrorCode unknownAppliedMethodErrorCode, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + TErrorCode unknownDatabase, + TErrorCode unknownCrossDatabaseData, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + TErrorCode unknownDataFormatErrorCode, + IClock clock, + 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 > clock.GetUtcNow().ToDateTimeOffset()) + { + 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()] + ) + ); + } + 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 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 + ) + ) + .ToImmutableArray() // Execute eagerly to have the data loader collect all keys first. + ); + foreach (var (index, database) in databases.Index()) + { + 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)], + dataFormatByIdDataLoader, + unknownDataFormatErrorCode, + cancellationToken + ); + if (validateRootResourceResult.Failed(out var dataFormat, out var validateRootResourceErrors)) + { + errors.AddRange(validateRootResourceErrors); + } + // Note that `dataFormat` is only `null`, when `validateRootResourceResult` failed. + if (errors.Count > 0 || dataFormat is null) + { + return new Result.Error( + NewPayload(null, errors) + ); + } + return new Result.Data(dataFormat); + } +} 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/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index c722140e..7cca1a4a 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -1,15 +1,36 @@ +using Database.Data; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed 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); + descriptor.Name(nameof(DataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .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 ceb50e8b..feb4652d 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -1,58 +1,59 @@ -using System; -using System.Linq; -using Database.GraphQl.Filters; -using HotChocolate.Configuration; +using Database.Data; +using Database.GraphQl.Entities; +using Database.GraphQl.GetHttpsResources; 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 abstract class DataFilterTypeBase + : AuditableEntityFilterType + 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); - // } + 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); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor + .Field(_ => _.Resources) + .Type>(); + descriptor.Field(_ => _.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 9b49bfae..5a73b80f 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,75 +1,43 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.GraphQl.Scalars; +using Database.Services; using HotChocolate; -using HotChocolate.Data; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +[ExtendObjectType(nameof(Query))] +public sealed class DataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class DataQueries + public Task GetDataAsync( + Guid id, + DataKind dataKind, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) { - [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. - [UseSorting] - public IEnumerable GetAllData( - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] 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()); - } - - public async Task GetDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] 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); - } + return DataQueriesBase.GetDataAsync( + id, + locale, + databaseContext => databaseContext.Data(dataKind), + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs new file mode 100644 index 00000000..1b1616e1 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -0,0 +1,136 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.GraphQl.Scalars; +using Database.Services; +using GreenDonut.Data; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Resolvers; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.DataX; + +public abstract class DataQueriesBase + where TData : class, IData +{ + protected static Task> GetAllDataAsync( + Func> getData, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return accessPolicyService.ApplyAsync( + databaseContext => getData(databaseContext).AsNoTracking() + .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); + }, + databaseContextFactory, + cancellationToken + ); + } + + protected static async Task> GetAllPendingDataAsync( + Func> getData, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return await Enumerable.Empty() + .AsQueryable() + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + return await accessPolicyService.ApplyAsync( + databaseContext => getData(databaseContext).AsNoTracking() + .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); + }, + databaseContextFactory, + cancellationToken + ); + } + + protected static Task HasDataAsync( + Func> getData, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return accessPolicyService.ApplyAsync( + databaseContext => getData(databaseContext).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); + }, + databaseContextFactory, + cancellationToken + ); + } + + internal static async Task GetDataAsync( + Guid id, + [GraphQLType] string? locale, + Func> getData, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + 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, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs new file mode 100644 index 00000000..46c33753 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -0,0 +1,168 @@ +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; +using HotChocolate.Authorization; + +namespace Database.GraphQl.DataX; + +public sealed class DataResolvers +{ + [UseFiltering] + [UseSorting] + public Task GetHttpsResourcesAsync( + [Parent] IData data, + IResolverContext resolverContext, + IHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(data.Id, cancellationToken); + } + + public GetHttpsResourceTree GetHttpsResourceTree( + [Parent] IData data + ) + { + return new GetHttpsResourceTree(data); + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task GetDataAccessPolicyAsync( + [Parent] IData data, + IResolverContext resolverContext, + IDataAccessPolicyByDataIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return null; + } + return await byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(data.Id, cancellationToken); + } + + 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); + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public static async Task IsNobodyAllowedAsync( + [Parent] IData data, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + IResolverContext resolverContext, + 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 IsEveryoneAllowedAsync( + [Parent] IData data, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return IsAccessAllowedAsync(data, null, null, null, databaseContext, authorization, resolverContext, cancellationToken); + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public static async Task IsAccessAllowedAsync( + [Parent] IData data, + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + IResolverContext resolverContext, + 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), + 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/DataSortType.cs b/backend/src/GraphQl/DataX/DataSortType.cs new file mode 100644 index 00000000..3efa5258 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataSortType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.DataX; + +public sealed class DataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + 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 new file mode 100644 index 00000000..5c8d0eba --- /dev/null +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -0,0 +1,23 @@ +using Database.Data; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.DataX; + +public abstract class DataSortTypeBase + : AuditableEntitySortType + where TData : IData, IAuditable +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + } +} diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 718232c3..d5657bb3 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,28 +1,56 @@ +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using Database.GraphQl.Scalars; using HotChocolate.Types; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class DataType(AppSettings appSettings) + : InterfaceType { - public sealed class DataType - : InterfaceType + public const string DatabaseIdFieldName = "databaseId"; + public const string TimestampFieldName = "timestamp"; + public const string ResourceTreeFieldName = "resourceTree"; + + 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(GraphQlConstants.IdFieldName) + .Type>(); + descriptor + .Field(GraphQlConstants.UuidFieldName) + .Type>(); + descriptor + .Field(_ => _.UpdatedAt) + .Name(TimestampFieldName); + descriptor + .Field(_ => _.CreatedAt); + descriptor + .Field(_ => _.Locale) + .Type>(); + descriptor + .Field(DatabaseIdFieldName) + .Type>() + .Resolve(_ => appSettings.DatabaseId); + descriptor + .Field(ResourceTreeFieldName) + .Type>>(); + descriptor + .Field(_ => _.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 new file mode 100644 index 00000000..7bd3eed1 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -0,0 +1,72 @@ +using System; +using Database.ApiRequests; +using Database.Data; +using Database.GraphQl.Entities; +using Database.GraphQl.Scalars; +using Database.Extensions; +using GreenDonut; +using HotChocolate.Types; + +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(_ => _.UpdatedAt) + .Name(DataType.TimestampFieldName); + descriptor + .Field(_ => _.Locale) + .Type>(); + descriptor + .Field(_ => _.Resources) + .Cost(1) + .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(nameof(DataResolvers.IsNobodyAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsNobodyAllowedAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(nameof(DataResolvers.IsEveryoneAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsEveryoneAllowedAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(nameof(DataResolvers.IsAccessAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!, default!, default!)); + descriptor + .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/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs new file mode 100644 index 00000000..15056ad5 --- /dev/null +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -0,0 +1,114 @@ +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 HotChocolate.Types; +using HotChocolate.Authorization; + +namespace Database.GraphQl.DataX; + +public sealed record DeleteDataInput( + Guid DataId, + DataKind DataKind +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataErrorCode +{ + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + NOT_PENDING +} + +public sealed record DeleteDataError( + DeleteDataErrorCode Code, + string Message, + IReadOnlyList Path +) : UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataPayload( + // [GraphQLType>>] + IReadOnlyCollection? Errors +) : Payload; + +[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); + + // [GraphQLType>] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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; + } + + 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.", + [] + )] + ); + } + + // 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(); + } + + context.Remove(data); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data, null); + } +} 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/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 034b6f60..b955f5bc 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -2,41 +2,48 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.GraphQl.GetHttpsResources; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate.Data; +using HotChocolate.Resolvers; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTree( + Data.IData data +) { - public sealed class GetHttpsResourceTree + public async Task GetRoot( + IHttpsResourceTreeRootByDataIdDataLoader byId, + CancellationToken cancellationToken + ) { - private readonly Data.DataX _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() - ); - } + return new GetHttpsResourceTreeRoot( + (await byId.LoadRequiredAsync(data.Id, cancellationToken)) + .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(); - } + [UseFiltering] + [UseSorting] + public async Task> GetNonRootVertices( + IResolverContext resolverContext, + IHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return ( + (await byId + .With(resolverContext.GetQueryContext()) + .LoadAsync(data.Id, cancellationToken) + ) ?? []) + .Select(_ => + new GetHttpsResourceTreeNonRootVertex(_) + ) + .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 7a02277e..97b038d9 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -1,37 +1,28 @@ using System; +using Database.Data; using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.DataX -{ - public sealed class GetHttpsResourceTreeNonRootVertex - : IGetHttpsResourceTreeVertex - { - [GraphQLType(typeof(NonNullType))] - public string VertexId - { - get => Data.GetHttpsResource.ConstructVertexId(Value.Id); - } +namespace Database.GraphQl.DataX; - public Data.GetHttpsResource Value { get; } +public sealed class GetHttpsResourceTreeNonRootVertex( + GetHttpsResource value +) +: IGetHttpsResourceTreeVertex +{ + [GraphQLType>] + public string ParentId => + GetHttpsResource.ConstructVertexId( + Value.ParentId + ?? throw new InvalidOperationException("Impossible! Each non-root vertex has a parent.") + ); - [GraphQLType(typeof(NonNullType))] - public string ParentId - { - get => Data.GetHttpsResource.ConstructVertexId( - Value.ParentId - ?? 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 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; } = value; } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs new file mode 100644 index 00000000..05b15fe4 --- /dev/null +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -0,0 +1,23 @@ +using Database.Data; +using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeNonRootVertexFilterType + : GetHttpsResourceFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.CalorimetricData).Ignore(); + descriptor.Field(_ => _.GeometricData).Ignore(); + descriptor.Field(_ => _.HygrothermalData).Ignore(); + descriptor.Field(_ => _.LifeCycleData).Ignore(); + descriptor.Field(_ => _.OpticalData).Ignore(); + descriptor.Field(_ => _.PhotovoltaicData).Ignore(); + } +} diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs new file mode 100644 index 00000000..55c525c2 --- /dev/null +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Sorting; + +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/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs deleted file mode 100644 index 228d3044..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX -{ - 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 - ) - { - } - } -} diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs index c69122f3..956377bc 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs @@ -1,23 +1,14 @@ +using Database.Data; using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.DataX -{ - public sealed class GetHttpsResourceTreeRoot : IGetHttpsResourceTreeVertex - { - [GraphQLType(typeof(NonNullType))] - public string VertexId - { - get => Data.GetHttpsResource.ConstructVertexId(Value.Id); - } +namespace Database.GraphQl.DataX; - public Data.GetHttpsResource Value { get; } +public sealed class GetHttpsResourceTreeRoot( + GetHttpsResource value + ) : IGetHttpsResourceTreeVertex +{ + [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); - public GetHttpsResourceTreeRoot( - Data.GetHttpsResource value - ) - { - Value = value; - } - } -} + 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 deleted file mode 100644 index 4d68de7b..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX -{ - 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 - ) - { - } - } -} diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs deleted file mode 100644 index 3c58ff11..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX -{ - 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 - ) - { - } - } -} diff --git a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs index 774fbe8a..448e1df3 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(typeof(IdType))] - 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/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs new file mode 100644 index 00000000..bc891acd --- /dev/null +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -0,0 +1,164 @@ +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 HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +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, + MISSING_ROOT_RESOURCE, + DUPLICATE_ROOT_RESOURCE, + RESOURCES_WITHOUT_FILE +} + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 errors = new List(); + var rootResources = + await context.GetHttpsResources.AsQueryable() + .Where(_ => _.ParentId == null) + .Where(_ => _.GetDataId(input.DataKind) == input.DataId) + .ToListAsync(cancellationToken); + if (rootResources.Count is 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 > 0) + { + 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 > 0) + { + return NewPayload(null, errors); + } + + 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); + } +} diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs new file mode 100644 index 00000000..475c5fd4 --- /dev/null +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -0,0 +1,126 @@ +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 HotChocolate.Authorization; + +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, + NOT_PUBLISHED +} + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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; + } + + 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); + + 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); + } +} diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs new file mode 100644 index 00000000..89ea232f --- /dev/null +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -0,0 +1,145 @@ +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.GraphQl.Scalars; +using Database.Enumerations; +using Database.Extensions; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; + +namespace Database.GraphQl.DataX; + +public sealed record UpdateDataInput( + [GraphQLType>] string Locale, + Guid DataId, + DataKind DataKind, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset 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 +) : Payload; + +[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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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); + } +} diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs new file mode 100644 index 00000000..a769526f --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -0,0 +1,39 @@ +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using HotChocolate; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace Database.GraphQl.Databases; + +[ExtendObjectType(nameof(Query))] +public sealed class DatabaseQueries +{ + public async Task GetDatabaseAsync( + AppSettings appSettings, + IDatabaseByIdDataLoader byId, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var database = await GraphQlRequestHelper.TransformExceptionsAsync( + () => byId.LoadAsync( + appSettings.DatabaseId, + cancellationToken + ), + resolverContext, + DatabaseDataLoader.GetGraphQlEndpoint(appSettings) + ); + 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; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs new file mode 100644 index 00000000..b1211b29 --- /dev/null +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -0,0 +1,46 @@ +using System.Threading; +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.AuthenticatedPolicy)] + public async Task UpdateDatabaseAsync( + UpdateDatabaseInput input, + UpdateDatabase updateDatabase, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var databasePayload = await GraphQlRequestHelper.TransformExceptionsAsync( + () => updateDatabase.Do( + input, + cancellationToken + ), + resolverContext, + updateDatabase.GetGraphQlEndpoint + ); + if (databasePayload is null) + { + return new UpdateDatabasePayload( + null, + [ + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + [] + ) + ] + ); + } + return databasePayload; + } +} diff --git a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs new file mode 100644 index 00000000..4ffca930 --- /dev/null +++ b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs @@ -0,0 +1,45 @@ +using System.Threading; +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.AuthenticatedPolicy)] + 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; + } +} diff --git a/backend/src/GraphQl/Edge.cs b/backend/src/GraphQl/Edge.cs index 17486d82..88bd4a64 100644 --- a/backend/src/GraphQl/Edge.cs +++ b/backend/src/GraphQl/Edge.cs @@ -2,28 +2,21 @@ using System.Threading; using System.Threading.Tasks; using GreenDonut; -using HotChocolate; -namespace Database.GraphQl -{ - public abstract class Edge - where TNodeByIdDataLoader : IDataLoader - { - private readonly Guid _nodeId; +namespace Database.GraphQl; - protected Edge( - Guid nodeId - ) - { - _nodeId = nodeId; - } +public abstract class Edge( + Guid nodeId + ) + where TNodeByIdDataLoader : IDataLoader +{ + private readonly Guid _nodeId = nodeId; - public async Task GetNodeAsync( - [DataLoader] 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))!; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs deleted file mode 100644 index 11e3a79c..00000000 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using GreenDonut; -using Microsoft.EntityFrameworkCore; -using Guid = System.Guid; - -namespace Database.GraphQl.Entities -{ - public abstract class AssociationsByAssociateIdDataLoader - : GroupedDataLoader - { - private readonly IDbContextFactory _dbContextFactory; - private readonly Func, IQueryable> _getAssociations; - private readonly Func _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 - ) - { - await using var dbContext = - _dbContextFactory.CreateDbContext(); - return ( - await _getAssociations(dbContext, ids) - .ToListAsync(cancellationToken) - .ConfigureAwait(false) - ) - .ToLookup(_getAssociateId); - } - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/AuditableEntityFilterType.cs b/backend/src/GraphQl/Entities/AuditableEntityFilterType.cs new file mode 100644 index 00000000..8fe8dfca --- /dev/null +++ b/backend/src/GraphQl/Entities/AuditableEntityFilterType.cs @@ -0,0 +1,60 @@ +using HotChocolate.Data.Filters; +using Database.Data; + +namespace Database.GraphQl.Entities; + +public abstract class AuditableEntityFilterType + : FilterInputType + where TEntity : IEntity, IAuditable +{ + protected override void Configure( + 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); + } + + // 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/Entities/AuditableEntitySortType.cs b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs new file mode 100644 index 00000000..33dd5ed0 --- /dev/null +++ b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs @@ -0,0 +1,20 @@ +using HotChocolate.Data.Sorting; +using Database.Data; + +namespace Database.GraphQl.Entities; + +public abstract class AuditableEntitySortType + : SortInputType + where TEntity : IEntity, IAuditable +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.BindFieldsExplicitly(); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + } +} diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs deleted file mode 100644 index 7a1cbdfb..00000000 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using GreenDonut; -using Microsoft.EntityFrameworkCore; -using Guid = System.Guid; - -namespace Database.GraphQl.Entities -{ - public abstract class EntityByIdDataLoader - : BatchDataLoader - where TEntity : class, Data.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; - } - - protected override async Task> LoadBatchAsync( - IReadOnlyList ids, - CancellationToken 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); - } - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index 47fe0a71..6dfd7111 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -1,39 +1,41 @@ using System; using GreenDonut; -using HotChocolate.Resolvers; 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 { - 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) => + base.Configure(descriptor); + 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.xmin) - .Type>() - .Name("version") - .Ignore(); - } + .DataLoader() + .LoadAsync(id, context.RequestAborted) + ); + descriptor + .Field(GraphQlConstants.UuidFieldName) + .Type>() + .Cost(0) + .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(GraphQlConstants.VersionFieldName) + .Ignore(); } } \ No newline at end of file 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/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs new file mode 100644 index 00000000..16773c2a --- /dev/null +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Database.Logging; +using HotChocolate; +using HotChocolate.Execution; +using HotChocolate.Execution.Instrumentation; +using HotChocolate.Resolvers; +using Microsoft.Extensions.Logging; +using HotChocolate.Language; +using System.Text; +using System.Globalization; + +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 = "Request error. Document: {Document}" + )] + public static partial void RequestError( + this ILogger logger, + IOperationDocument? document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Resolver error. Field: '{FieldName}'. Document: {Document}" + )] + public static partial void ResolverError( + this ILogger logger, + string fieldName, + DocumentNode document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error, + Exception? exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Resolver error. Field: '{FieldName}'. Document: {Document}" + )] + public static partial void ResolverError( + this ILogger logger, + Exception? exception, + string fieldName, + IOperationDocument? document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Subscription event error. Id: '{SubscriptionId}'. Operation: {Document}" + )] + public static partial void SubscriptionEventError( + this ILogger logger, + Exception exception, + ulong subscriptionId, + IOperationDocument? document + ); + + [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, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Validation error. Document: {Document}")] + public static partial void ValidationError( + this ILogger logger, + Exception? exception, + IOperationDocument? document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Operation error. Document: {Document}\nVariables: {Variables}")] + public static partial void OperationError( + this ILogger logger, + IOperationDocument? document, + string? variables, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error, + Exception? exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Unexpected execution error. Document: {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 +// 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(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( + RequestContext context, + Exception error + ) + { + logger.RequestError(error, context.Request.Document); + base.RequestError(context, error); + } + + public override void RequestError( + RequestContext context, + IError error + ) + { + logger.RequestError(context.Request.Document, error); + base.RequestError(context, error); + } + + public override void ResolverError( + IMiddlewareContext context, + IError error + ) + { + logger.ResolverError(context.Selection.Field.Name, context.Operation.Document, error, error.Exception); + base.ResolverError(context, error); + } + + public override void ResolverError( + RequestContext context, + ISelection selection, + IError error + ) + { + logger.ResolverError(error.Exception, selection.Field.Name, context.Request?.Document, error); + base.ResolverError(context, selection, error); + } + + public override void SubscriptionEventError( + RequestContext context, + ulong subscriptionId, + Exception exception + ) + { + logger.SubscriptionEventError(exception, subscriptionId, context.Request.Document); + base.SubscriptionEventError(context, subscriptionId, exception); + } + + 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( + RequestContext 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, RequestContext 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; + } + 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; + } + + public void Dispose() + { + if (logger.IsEnabled(LogLevel.Debug)) + { + 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.OperationDocumentInfo.Document.ToString(true), + StringifyVariables() + ); +#pragma warning restore CA1873 + } + } + // when the request is finished it will dispose the activity scope + if (context.Result is OperationResult { Errors.Count: > 0 } operationResult) + { + foreach (var error in operationResult.Errors) + { + logger.OperationError(context.Request.Document, StringifyVariables(), error, error.Exception); + } + } + } + } +} \ 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/Extensions/PageExtensions.cs b/backend/src/GraphQl/Extensions/PageExtensions.cs new file mode 100644 index 00000000..ec879931 --- /dev/null +++ b/backend/src/GraphQl/Extensions/PageExtensions.cs @@ -0,0 +1,71 @@ +using System.Threading.Tasks; +using System.Diagnostics.Contracts; +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 +{ + [Pure] + public static async ValueTask GetTotalCountAsync( + this Task?> pagePromise + ) + { + return (await pagePromise)?.TotalCount ?? 0; + } + + [Pure] + 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() + ); + } + + // [Pure] + // 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); + // } + + // [Pure] + // 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 new file mode 100644 index 00000000..88a2fdeb --- /dev/null +++ b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.Contracts; +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 + // 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; + 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 + [Pure] + 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/FileMetaInformationInput.cs b/backend/src/GraphQl/FileMetaInformationInput.cs index 4957734f..816cbfcf 100644 --- a/backend/src/GraphQl/FileMetaInformationInput.cs +++ b/backend/src/GraphQl/FileMetaInformationInput.cs @@ -1,9 +1,18 @@ using System; +using Database.Data; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed record FileMetaInformationInput( + string[] Path, + Guid DataFormatId +) { - public 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/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/Filters/AdditionalFilterOperations.cs b/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs index 9217176f..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 +namespace Database.GraphQl.Filters; + +public static class AdditionalFilterOperations { - 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/INotField.cs b/backend/src/GraphQl/Filters/INotField.cs index c0e48a4b..9b24e3f3 100644 --- a/backend/src/GraphQl/Filters/INotField.cs +++ b/backend/src/GraphQl/Filters/INotField.cs @@ -1,12 +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 1a8437cc..d7234d79 100644 --- a/backend/src/GraphQl/Filters/NotField.cs +++ b/backend/src/GraphQl/Filters/NotField.cs @@ -1,42 +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 - : 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 InputFieldDefinition CreateDefinition( - IDescriptorContext context, - string? scope) => - 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/QueryableComparableInClosedIntervalHandler.cs b/backend/src/GraphQl/Filters/QueryableComparableInClosedIntervalHandler.cs new file mode 100644 index 00000000..33d5523a --- /dev/null +++ b/backend/src/GraphQl/Filters/QueryableComparableInClosedIntervalHandler.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq.Expressions; +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 QueryableComparableInClosedIntervalHandler +: QueryableComparableOperationHandler +{ + public QueryableComparableInClosedIntervalHandler( + 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.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 diff --git a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs new file mode 100644 index 00000000..1df78ffc --- /dev/null +++ b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs @@ -0,0 +1,185 @@ +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 +{ + 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 LocalDateTimeFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"LocalDateTime{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 DurationFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Duration{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 NonNegativeIntFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"NonNegativeInt{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 UriFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Url{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs deleted file mode 100644 index 44298992..00000000 --- a/backend/src/GraphQl/ForkingConnection.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using GreenDonut; -using HotChocolate; - -namespace Database.GraphQl -{ - public abstract class ForkingConnection - where TSubject : Data.IEntity - where TSomeAssociationsByAssociateIdDataLoader : IDataLoader - where TOtherAssociationsByAssociateIdDataLoader : IDataLoader - { - protected TSubject Subject { get; } - private readonly bool _useFirstDataLoader; - private readonly Func _createEdge; - - protected ForkingConnection( - TSubject subject, - bool useFirstDataLoader, - Func createEdge - ) - { - Subject = subject; - _useFirstDataLoader = useFirstDataLoader; - _createEdge = createEdge; - } - - public Task> GetEdgesAsync( - [DataLoader] TSomeAssociationsByAssociateIdDataLoader someDataLoader, - [DataLoader] TOtherAssociationsByAssociateIdDataLoader otherDataLoader, - CancellationToken cancellationToken - ) - { - return _useFirstDataLoader - ? GetEdgesAsync(someDataLoader, cancellationToken) - : GetEdgesAsync(otherDataLoader, cancellationToken); - } - - private async Task> GetEdgesAsync( - TDataLoader dataLoader, - CancellationToken cancellationToken - ) - where TDataLoader : IDataLoader - { - return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) - ) - .Select(_createEdge); - } - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs new file mode 100644 index 00000000..b30ef610 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -0,0 +1,180 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.GeometricDataX; + +public sealed record CreateGeometricDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, + double[] Widths, + double[] Heights, + double[] Thicknesses +) : IValidateCreateInput +{ + public GeometricData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new GeometricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + Widths, + Heights, + Thicknesses + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public 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 +} + +public sealed record CreateGeometricDataError( + CreateGeometricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateGeometricDataPayload( + GeometricData? GeometricData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreateGeometricDataMutation +: CreateDataMutationBase +{ + // [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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateGeometricDataAsync( + CreateGeometricDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateGeometricDataErrorCode.UNAUTHENTICATED, + CreateGeometricDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreateGeometricDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_DATABASE, + CreateGeometricDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var geometricData = input.ToDomainModel( + currentUserOrInstitution.CurrentUser?.Uuid, + dataFormat.Extension + ); + context.GeometricData.Add(geometricData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + geometricData, + CreateGeometricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(geometricData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(geometricData, null); + } +} diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs new file mode 100644 index 00000000..f6c75a11 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -0,0 +1,38 @@ +using Database.Data; +using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataFilterType + : DataFilterTypeBase +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(GeometricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.Thicknesses); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); + } +} \ 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 new file mode 100644 index 00000000..8227f0f6 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -0,0 +1,107 @@ +using System; +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.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GeometricDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class GeometricDataQueries +: DataQueriesBase +{ + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllGeometricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetAllDataAsync( + databaseContext => databaseContext.GeometricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingGeometricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.GeometricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task HasGeometricDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.GeometricData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetGeometricDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.GeometricData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); + } +} \ 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..d0984d79 --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs new file mode 100644 index 00000000..c1caea06 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Types; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataType + : DataTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ 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 47ff5f6b..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.GetHttpsResources -{ - public sealed class CreateGetHttpsResourceError - : GraphQl.UserErrorBase - { - public CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs deleted file mode 100644 index 266a92e6..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Database.GraphQl.GetHttpsResources -{ - public enum CreateGetHttpsResourceErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs deleted file mode 100644 index 654b64ce..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Database.GraphQl.GetHttpsResources -{ - public record CreateGetHttpsResourceInput( - string AccessToken, - string Description, - string HashValue, - Guid DataFormatId, - Guid DataId, - Enumerations.DataKind DataKind, - Guid? ParentId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod - ); -} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs new file mode 100644 index 00000000..552851be --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -0,0 +1,204 @@ +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 Database.Utilities; +using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed record CreateGetHttpsResourceInput( + Guid DataId, + DataKind DataKind, + string Description, + Guid DataFormatId, + Guid ParentId, + IReadOnlyList ArchivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod +) : IIdentifyDataInput, IValidateGetHttpsResourceInput +{ + public GetHttpsResource ToDomainModel(string? fileExtension) + { + return new( + Description, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + DataFormatId, + fileExtension, + 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() + ); + } +} + +[SuppressMessage("Naming", "CA1707")] +public enum CreateGetHttpsResourceErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATA, + UNKNOWN_PARENT, + ILLEGAL_PARENT, + UNKNOWN_DATA_FORMAT, + UNKNOWN_METHOD, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateGetHttpsResourcePayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; + +[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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateGetHttpsResourceAsync( + CreateGetHttpsResourceInput input, + ApplicationDbContext context, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + ResponseApprovalService responseApprovalService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateGetHttpsResourceErrorCode.UNAUTHENTICATED, + CreateGetHttpsResourceErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + 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()] + ) + ); + } + 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); + } + 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 > 0 || dataFormat is null) + { + return NewPayload(null, errors); + } + + var getHttpsResource = input.ToDomainModel(dataFormat?.Extension); + context.GetHttpsResources.Add(getHttpsResource); + await context.SaveChangesAsync(cancellationToken); + + 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); + } +} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs deleted file mode 100644 index 503c5d2d..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Database.GraphQl.GetHttpsResources -{ - public sealed class CreateGetHttpsResourcePayload - : GetHttpsResourcePayload - { - public CreateGetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource - ) - : base(getHttpsResource) - { - } - - public CreateGetHttpsResourcePayload( - CreateGetHttpsResourceError error - ) - : base(error) - { - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs new file mode 100644 index 00000000..adecda71 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -0,0 +1,160 @@ +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 HotChocolate.Types; +using HotChocolate.Authorization; +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 > 0) + { + 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); + } +} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs deleted file mode 100644 index 44e93181..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GreenDonut; -using Database.GraphQl.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.GetHttpsResources -{ - public sealed class GetHttpsResourceByIdDataLoader - : EntityByIdDataLoader - { - public GetHttpsResourceByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GetHttpsResources - ) - { - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs deleted file mode 100644 index f3373046..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.GetHttpsResources -{ - 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 - ) - { - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs new file mode 100644 index 00000000..77155783 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs @@ -0,0 +1,104 @@ +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.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 + ); + } + + [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/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 229bd3e2..eb26df59 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,24 +1,36 @@ +using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public class GetHttpsResourceFilterType + : AuditableEntityFilterType { - 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); - } + 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); + descriptor.Field(_ => _.AppliedConversionMethod); + descriptor.Field(_ => _.ArchivedFilesMetaInformation); + descriptor.Field(_ => _.Parent); + descriptor.Field(_ => _.DataId); + descriptor.Field(_ => _.DataKind); + // 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/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs deleted file mode 100644 index 6f74ab54..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Extensions; -using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; -using HotChocolate.Types; - -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( - CreateGetHttpsResourceInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - [ScopedService] 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 - ) - ); - context.GetHttpsResources.Add(getHttpsResource); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreateGetHttpsResourcePayload(getHttpsResource); - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs deleted file mode 100644 index 9b78c03c..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.GetHttpsResources -{ - public abstract class GetHttpsResourcePayload - : Payload - where TGetHttpsResourceError : IUserError - { - public Data.GetHttpsResource? GetHttpsResource { get; } - public IReadOnlyCollection? Errors { get; } - - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource - ) - { - GetHttpsResource = getHttpsResource; - } - - protected GetHttpsResourcePayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected GetHttpsResourcePayload( - TGetHttpsResourceError error - ) - : this(new[] { error }) - { - } - - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource, - IReadOnlyCollection errors - ) - { - GetHttpsResource = getHttpsResource; - Errors = errors; - } - - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource, - TGetHttpsResourceError error - ) - : this( - getHttpsResource, - new[] { error } - ) - { - } - } -} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 605502b7..c199197a 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -1,38 +1,91 @@ +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.GraphQl.Extensions; +using GreenDonut.Data; +using HotChocolate.Authorization; using HotChocolate.Data; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +[ExtendObjectType(nameof(Query))] +public sealed class GetHttpsResourceQueries { - [ExtendObjectType(nameof(Query))] - public sealed class GetHttpsResourceQueries + [UsePaging] + [UseFiltering] + [UseSorting] + public ValueTask> GetGetHttpsResources( + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + 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) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async ValueTask> GetPendingGetHttpsResources( + ApplicationDbContext context, + CommonAuthorization authorization, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) { - [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] - [UseSorting] - public IQueryable GetGetHttpsResources( - [ScopedService] Data.ApplicationDbContext context - ) + if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return context.GetHttpsResources; + return await Enumerable.Empty().AsQueryable() + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } + 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) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } - public Task GetGetHttpsResourceAsync( - Guid id, - GetHttpsResourceByIdDataLoader byId, - CancellationToken cancellationToken - ) + public async Task GetGetHttpsResourceAsync( + Guid id, + IGetHttpsResourceByIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (await authorization.IsDatabaseOperator(cancellationToken)) { - return byId.LoadAsync( - id, - 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); } } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 5c8ae47d..0cddf177 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,86 +1,70 @@ using System; using System.Threading; using System.Threading.Tasks; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; +using Database.ApiRequests; +using Database.Controllers; +using Database.Data; +using Database.GraphQl.DataX; +using GreenDonut; using HotChocolate; +using Microsoft.AspNetCore.Routing; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceResolvers { - public sealed class GetHttpsResourceResolvers + public Task GetData( + [Parent] GetHttpsResource getHttpsResource, + IDataByIdAndKindDataLoader dataByIdAndKindDataLoader, + 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; - } + return dataByIdAndKindDataLoader.LoadRequiredAsync( + (getHttpsResource.DataId, getHttpsResource.DataKind), + cancellationToken + ); + } - public Uri GetLocator( - [Parent] Data.GetHttpsResource getHttpsResource, - [Service] AppSettings appSettings - ) + public Uri GetLocator( + [Parent] GetHttpsResource getHttpsResource, + AppSettings appSettings, + LinkGenerator linkGenerator + ) + { + return new UriBuilder(appSettings.Uri) { - return new Uri($"{appSettings.Host}/api/resources/{getHttpsResource.Id}"); - } - - public async Task GetParent( - [Parent] Data.GetHttpsResource getHttpsResource, - GetHttpsResourceByIdDataLoader byId, - CancellationToken cancellationToken + Path = linkGenerator.GetPathByRouteValues( + routeName: GetHttpsResourcesController.ConstructGetActionRouteName(getHttpsResource), + values: GetHttpsResourcesController.CreateGetActionRouteValues(getHttpsResource) + ) + }.Uri; + } - ) - { - // 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 async Task GetParent( + [Parent] GetHttpsResource getHttpsResource, + IGetHttpsResourceByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return getHttpsResource.ParentId is null + ? null + : await byId.LoadAsync(getHttpsResource.ParentId ?? Guid.Empty, cancellationToken)!; + } - public Task GetChildren( - [Parent] Data.GetHttpsResource getHttpsResource, - GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, - CancellationToken cancellationToken + public Task GetChildren( + [Parent] GetHttpsResource getHttpsResource, + IHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(getHttpsResource.Id, 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/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs new file mode 100644 index 00000000..0d2908ef --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -0,0 +1,22 @@ +using Database.Data; +using Database.GraphQl.Entities; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.GetHttpsResources; + +public class GetHttpsResourceSortType + : AuditableEntitySortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.HashValue); + 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 3f64c1c1..9731f5a7 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,57 +1,98 @@ +using Database.ApiRequests; +using Database.Data; +using Database.GraphQl.Entities; +using Database.Extensions; 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") + .Cost(0) + .ResolveWith(t => t.GetLocator(default!, default!, default!)); + descriptor + .Field(_ => _.FileName) + .Ignore(); + descriptor + .Field(_ => _.FilePath) + .Ignore(); + descriptor + .Field(_ => _.FileExtension) + .Ignore(); + descriptor + .Field(_ => _.ParentId) + .Ignore(); + descriptor + .Field(_ => _.Parent) + .Cost(0) + .ResolveWith(t => t.GetParent(default!, default!, default!)); + descriptor + .Field(_ => _.Children) + .Cost(0) + .ResolveWith(t => t.GetChildren(default!, default!, default!)); + descriptor + .Field(_ => _.DataId) + .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 => + t.GetData(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/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs new file mode 100644 index 00000000..332dc1c5 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -0,0 +1,152 @@ +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.Extensions; +using Database.GraphQl.Extensions; +using Database.Services; +using GreenDonut.Data; +using HotChocolate.Data; +using HotChocolate.Authorization; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Database.GraphQl.GetHttpsResources; + +[SuppressMessage("Naming", "CA1707")] +public enum RecomputeGetHttpsResourceHashValuesErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + FAILED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection? GetHttpsResources, + 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 Log +{ + [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 +: 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] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task RecomputeGetHttpsResourceHashValuesAsync( + ApplicationDbContext context, + IResolverContext resolverContext, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ILogger logger, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var resources = + await context.GetHttpsResourcesWithData + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToListAsync(cancellationToken); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + resources, + cancellationToken, + async (resource, cancellationToken) => + { + 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( + 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 NewPayload(resources, errors); + } + return NewPayload(resources, null); + } +} diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs new file mode 100644 index 00000000..0e02a888 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -0,0 +1,170 @@ +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.Extensions; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 > 0) + { + 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); + } +} diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs new file mode 100644 index 00000000..3938e911 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -0,0 +1,159 @@ +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.Extensions; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 > 0 || dataFormat is null) + { + return NewPayload(null, errors); + } + + resource.UpdateChild( + input.Description, + input.DataFormatId, + dataFormat.Extension, + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToList(), + 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); + } +} diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs new file mode 100644 index 00000000..092665ea --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -0,0 +1,146 @@ +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.Extensions; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + 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 > 0 || dataFormat is null) + { + return NewPayload(null, errors); + } + + resource.UpdateRoot( + input.Description, + input.DataFormatId, + dataFormat.Extension, + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToList() + ); + 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); + } +} diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs new file mode 100644 index 00000000..30a0470a --- /dev/null +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -0,0 +1,15 @@ +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 IdFieldName = "id"; + 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/CreateHygrothermalDataError.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs deleted file mode 100644 index fd0c6528..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.HygrothermalDataX -{ - public sealed class CreateHygrothermalDataError - : GraphQl.UserErrorBase - { - public CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs deleted file mode 100644 index 42815def..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Database.GraphQl.HygrothermalDataX -{ - public enum CreateHygrothermalDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs deleted file mode 100644 index 8c0a1cde..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Types; - -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, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource - ); -} diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs new file mode 100644 index 00000000..4d8b2ff4 --- /dev/null +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -0,0 +1,173 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.HygrothermalDataX; + +public sealed record CreateHygrothermalDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource +) : IValidateCreateInput +{ + public HygrothermalData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new HygrothermalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public 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 +} + +public sealed record CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateHygrothermalDataPayload( + HygrothermalData? HygrothermalData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreateHygrothermalDataMutation +: CreateDataMutationBase +{ + // [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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateHygrothermalDataAsync( + CreateHygrothermalDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateHygrothermalDataErrorCode.UNAUTHENTICATED, + CreateHygrothermalDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreateHygrothermalDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_DATABASE, + CreateHygrothermalDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var hygrothermalData = input.ToDomainModel( + currentUserOrInstitution.CurrentUser?.Uuid, + dataFormat.Extension + ); + context.HygrothermalData.Add(hygrothermalData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + hygrothermalData, + CreateHygrothermalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(hygrothermalData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(hygrothermalData, null); + } +} diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs deleted file mode 100644 index 30995aa9..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Database.GraphQl.HygrothermalDataX -{ - public sealed class CreateHygrothermalDataPayload - : HygrothermalDataPayload - { - public CreateHygrothermalDataPayload( - Data.HygrothermalData hygrothermalData - ) - : base(hygrothermalData) - { - } - - public CreateHygrothermalDataPayload( - CreateHygrothermalDataError error - ) - : base(error) - { - } - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs deleted file mode 100644 index b94edfe3..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GreenDonut; -using Database.GraphQl.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.HygrothermalDataX -{ - public sealed class HygrothermalDataByIdDataLoader - : EntityByIdDataLoader - { - public HygrothermalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.HygrothermalData - ) - { - } - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index 853cf18b..e8605312 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -1,17 +1,37 @@ +using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; 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.Name(nameof(HygrothermalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); } -} +} \ 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/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs deleted file mode 100644 index fdf2fcff..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Extensions; -using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; -using HotChocolate.Types; - -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( - CreateHygrothermalDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - [ScopedService] 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, - // 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(), - 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 - ) - ).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); - } - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs deleted file mode 100644 index 57820f4d..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.HygrothermalDataX -{ - public abstract class HygrothermalDataPayload - : Payload - where THygrothermalDataError : IUserError - { - public Data.HygrothermalData? HygrothermalData { get; } - public IReadOnlyCollection? Errors { get; } - - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData - ) - { - HygrothermalData = hygrothermalData; - } - - protected HygrothermalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected HygrothermalDataPayload( - THygrothermalDataError error - ) - : this(new[] { error }) - { - } - - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData, - IReadOnlyCollection errors - ) - { - HygrothermalData = hygrothermalData; - Errors = errors; - } - - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData, - THygrothermalDataError error - ) - : this( - hygrothermalData, - new[] { error } - ) - { - } - } -} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 00be0ec6..2989dd8c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -1,45 +1,107 @@ using System; -using System.Linq; 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.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class HygrothermalDataQueries +: DataQueriesBase { - [ExtendObjectType(nameof(Query))] - public sealed class HygrothermalDataQueries + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllHygrothermalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) { - [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] - [UseSorting] - public IQueryable GetAllHygrothermalData( - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.HygrothermalData; - } + return GetAllDataAsync( + databaseContext => databaseContext.HygrothermalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingHygrothermalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.HygrothermalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } - public Task GetHygrothermalDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - HygrothermalDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + [UseFiltering] + public Task HasHygrothermalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.HygrothermalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetHygrothermalDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.HygrothermalData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); } -} +} \ 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 ff18570e..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.HygrothermalDataX -{ - public sealed class HygrothermalDataResolvers - { - public Task GetGetHttpsResources( - [Parent] Data.HygrothermalData hygrothermalData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(hygrothermalData.Id, cancellationToken); - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.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/HygrothermalDataSortType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs new file mode 100644 index 00000000..f1de2d25 --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs index f972aa40..9f5ec67d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs @@ -1,28 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; using HotChocolate.Types; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataType + : DataTypeBase { - 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); } -} +} \ 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/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs new file mode 100644 index 00000000..f6c8e27f --- /dev/null +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.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.Institutions; + +[ExtendObjectType(nameof(Query))] +public sealed class InstitutionQueries +{ + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentInstitutionAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrApplicationAsync( + (user, _) => Task.FromResult(null), + (application, _) => Task.FromResult(application.Owner), + cancellationToken + ); + } +} \ 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..05b17ab3 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -0,0 +1,173 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +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, + DateTimeOffset 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)); + data.AccessPolicy = new DataAccessPolicy(); + 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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateLifeCycleDataAsync( + CreateLifeCycleDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateLifeCycleDataErrorCode.UNAUTHENTICATED, + CreateLifeCycleDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, 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, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var lifeCycleData = input.ToDomainModel( + currentUserOrInstitution.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); + } +} diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs new file mode 100644 index 00000000..4a470170 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs @@ -0,0 +1,37 @@ +using Database.Data; +using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; +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); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); + } +} \ 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 new file mode 100644 index 00000000..1430575b --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -0,0 +1,107 @@ +using System; +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.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.LifeCycleDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class LifeCycleDataQueries +: DataQueriesBase +{ + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllLifeCycleDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetAllDataAsync( + databaseContext => databaseContext.LifeCycleData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingLifeCycleDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.LifeCycleData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task HasLifeCycleDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.LifeCycleData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetLifeCycleDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.LifeCycleData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + 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..01dd73b5 --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs new file mode 100644 index 00000000..0aebaf0e --- /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/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs deleted file mode 100644 index d8162800..00000000 --- a/backend/src/GraphQl/LocaleType.cs +++ /dev/null @@ -1,58 +0,0 @@ -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 -{ - /// - /// [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]+)?$"; - - /// - /// 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( - NameString name, - string? description = null, - BindingBehavior bind = BindingBehavior.Explicit) - : base( - name, - _validationPattern, - description, - RegexOptions.Compiled | RegexOptions.IgnoreCase, - bind) - { - } - } -} diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs deleted file mode 100644 index f62253f3..00000000 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -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 -{ - // 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 - ) - { - _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.LogError(exception, $"During execution of the query {context.Request.Query} the exception {exception} occurred."); - } - - 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.LogError(exception, $"During execution of the subscription operation {context.Subscription.Operation.Print()} 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."); - } - - 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.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.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 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 != null) - { - var variablesConcrete = _context.Variables!.ToList(); - if (variablesConcrete.Count > 0) - { - stringBuilder.AppendFormat($"Variables {Environment.NewLine}"); - try - { - foreach (var variableValue in _context.Variables!) - { - 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}"); - } - } - catch - { - // all input type records will land here. - stringBuilder.Append(" Formatting Variables Error. Continuing..."); - stringBuilder.AppendFormat($"{Environment.NewLine}"); - } - } - } - s_queryTimer.Stop(); - stringBuilder.AppendFormat($"Ellapsed time for query is {s_queryTimer.Elapsed.TotalMilliseconds:0.#} milliseconds."); - _logger.LogInformation(stringBuilder.ToString()); - } - } - } - } -} \ 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..5b5e31aa --- /dev/null +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -0,0 +1,176 @@ +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.Json; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace Database.GraphQl.Methods; + +[SuppressMessage("Naming", "CA1707")] +public enum CalculateMethodErrorCode +{ + UNKNOWN_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, + UNSUPPORTED_DATA_FORMAT, + HASH_VALUE_CALCULATION_FAILED, + HASH_VALUE_MISMATHCH +} + +public sealed record CalculateMethodError( + CalculateMethodErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CalculateMethodPayload( + JsonElement? Result, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Query))] +public sealed class MethodQueries +{ + public async Task CalculateMethodWithDataUploadAsync( + Guid methodId, + [GraphQLType>] IFile data, + MethodFactory methodFactory, + CancellationToken cancellationToken + ) + { + var method = methodFactory.GetMethod(methodId); + if (method is null) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [nameof(methodId)] + )] + ); + } + using var stream = data.OpenReadStream(); + using var jsonData = await JsonDocument.ParseAsync( + stream, + JsonDocumentSettings.Lax, + cancellationToken + ); + var result = method.Calculate(jsonData.RootElement); + return new CalculateMethodPayload(result, null); + } + + public async Task CalculateMethodAsync( + Guid methodId, + CrossDatabaseDataReferenceInput dataReference, + AppSettings appSettings, + MethodFactory methodFactory, + ApiRequestService apiRequestService, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKind, + QueryData queryData, + IResolverContext resolverContext, + UrlEncoder urlEncoder, + CancellationToken cancellationToken + ) + { + var method = methodFactory.GetMethod(methodId); + if (method is null) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [nameof(methodId)] + )] + ); + } + // We fetch the data through the metabase because, if possible, it adds + // sanitized authorization headers. + var database = await GraphQlRequestHelper.TransformExceptionsAsync( + () => dataByDatabaseAndIdAndKind.LoadAsync( + (dataReference.DatabaseId, dataReference.DataId, dataReference.DataKind), + cancellationToken + ), + resolverContext, + DatabaseDataLoader.GetGraphQlEndpoint(appSettings) + ); + if (database is null) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_DATABASE, + $"The database is unknown.", + [nameof(dataReference), nameof(dataReference.DatabaseId).ToLowerFirst()] + )] + ); + } + if (database.Data is null) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + 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)] + )] + ); + } + // 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 + ); + 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); + } +} \ No newline at end of file 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/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs new file mode 100644 index 00000000..4acb8937 --- /dev/null +++ b/backend/src/GraphQl/MutationBase.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using static Database.ApiRequests.QueryCurrentUserOrApplication; + +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 + ) + { + if (!await authorization.IsAuthenticated(cancellationToken)) + { + return new Result.Error( + NewPayload(null, [ + NewError( + unauthenticatedErrorCode, + "You are not authenticated.", + [] + ) + ]) + ); + } + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return new Result.Error( + NewPayload(null, [ + NewError( + unauthorizedErrorCode, + "You are not authorized to perform this mutation in this database.", + [] + ) + ]) + ); + } + return new Result.Data( + await authorization.FetchCurrentUserOrApplicationAsync(cancellationToken) + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs index a785ee6e..12eb7188 100644 --- a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs @@ -1,17 +1,18 @@ +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.Name(nameof(NamedMethodArgumentFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.Name); + // TODO descriptor.Field(_ => _.Value); } } diff --git a/backend/src/GraphQl/NamedMethodArgumentInput.cs b/backend/src/GraphQl/NamedMethodArgumentInput.cs index 9c322b7f..b22556db 100644 --- a/backend/src/GraphQl/NamedMethodArgumentInput.cs +++ b/backend/src/GraphQl/NamedMethodArgumentInput.cs @@ -1,10 +1,18 @@ -// using HotChocolate.Types; -// using HotChocolate; +using System.Text.Json; +using Database.Data; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed record NamedMethodArgumentInput( + string Name, + JsonElement Value +) { - public record NamedMethodArgumentInput( - string Name - // TODO [GraphQLType(nameof(NonNullType))] object 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/NamedMethodArgumentSortType.cs b/backend/src/GraphQl/NamedMethodArgumentSortType.cs new file mode 100644 index 00000000..5bac8f21 --- /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(_ => _.Name); + // TODO descriptor.Field(_ => _.Value); + } +} diff --git a/backend/src/GraphQl/NamedMethodArgumentType.cs b/backend/src/GraphQl/NamedMethodArgumentType.cs index fd6f80f0..92729ba9 100644 --- a/backend/src/GraphQl/NamedMethodArgumentType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentType.cs @@ -1,192 +1,14 @@ +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 - ) - { - // 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. - descriptor - .Field(x => x.Value) - .Type>() - .Resolve(context => - "TODO" - // context.Parent() - // .Value - ); - } - - // 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 - // ) - // ) - // }; - // } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodSourceInput.cs b/backend/src/GraphQl/NamedMethodSourceInput.cs index bc802f1d..8df5bc72 100644 --- a/backend/src/GraphQl/NamedMethodSourceInput.cs +++ b/backend/src/GraphQl/NamedMethodSourceInput.cs @@ -1,7 +1,17 @@ -namespace Database.GraphQl +using Database.Data; + +namespace Database.GraphQl; + +public sealed record NamedMethodSourceInput( + string Name, + CrossDatabaseDataReferenceInput Value +) { - public 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/Numerations/NumerationInput.cs b/backend/src/GraphQl/Numerations/NumerationInput.cs new file mode 100644 index 00000000..b2479108 --- /dev/null +++ b/backend/src/GraphQl/Numerations/NumerationInput.cs @@ -0,0 +1,7 @@ +namespace Database.GraphQl.Numerations; + +public sealed record NumerationInput( + 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..ffa58620 --- /dev/null +++ b/backend/src/GraphQl/Numerations/NumerationType.cs @@ -0,0 +1,14 @@ +using Database.Data; +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/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/OpticalDataX/CreateOpticalDataError.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs deleted file mode 100644 index 62aadbc0..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.OpticalDataX -{ - public sealed class CreateOpticalDataError - : GraphQl.UserErrorBase - { - public CreateOpticalDataError( - CreateOpticalDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } - } -} diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs deleted file mode 100644 index 8567ad14..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Database.GraphQl.OpticalDataX -{ - public enum CreateOpticalDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs deleted file mode 100644 index 37519525..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Types; - -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, - 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 - ); -} diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs new file mode 100644 index 00000000..196764df --- /dev/null +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -0,0 +1,195 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.OpticalDataX; + +public sealed record CreateOpticalDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset 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 +) : IValidateCreateInput +{ + public OpticalData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new OpticalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + Type, + Subtype, + CoatedSide ?? Enumerations.CoatedSide.UNKNOWN, + AppliedMethod.ToDomainModel(), + NearnormalHemisphericalVisibleTransmittances, + NearnormalHemisphericalVisibleReflectances, + NearnormalHemisphericalSolarTransmittances, + NearnormalHemisphericalSolarReflectances, + InfraredEmittances, + ColorRenderingIndices, + CielabColors.Select(c => c.ToDomainModel()).ToList() + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + data.AccessPolicy = new DataAccessPolicy(); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public 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 +} + +public sealed record CreateOpticalDataError( + CreateOpticalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateOpticalDataPayload( + OpticalData? OpticalData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreateOpticalDataMutation +: CreateDataMutationBase +{ + 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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateOpticalDataAsync( + CreateOpticalDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateOpticalDataErrorCode.UNAUTHENTICATED, + CreateOpticalDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreateOpticalDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_DATABASE, + CreateOpticalDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var opticalData = input.ToDomainModel( + currentUserOrInstitution.CurrentUser?.Uuid, + dataFormat.Extension + ); + 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/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs deleted file mode 100644 index 55f54d15..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Database.GraphQl.OpticalDataX -{ - public sealed class CreateOpticalDataPayload - : OpticalDataPayload - { - public CreateOpticalDataPayload( - Data.OpticalData opticalData - ) - : base(opticalData) - { - } - - public CreateOpticalDataPayload( - CreateOpticalDataError error - ) - : base(error) - { - } - } -} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs deleted file mode 100644 index 5f805eda..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GreenDonut; -using Database.GraphQl.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.OpticalDataX -{ - public sealed class OpticalDataByIdDataLoader - : EntityByIdDataLoader - { - public OpticalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.OpticalData - ) - { - } - } -} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 95278a05..e114ffa9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -1,24 +1,47 @@ +using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; 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.Name(nameof(OpticalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + 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(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); } -} +} \ 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/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs deleted file mode 100644 index a4d97a4e..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Extensions; -using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; -using HotChocolate.Types; - -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( - CreateOpticalDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - [ScopedService] 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, - // 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(), - 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 - ) - ).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); - } - } -} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs deleted file mode 100644 index 90617183..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.OpticalDataX -{ - public abstract class OpticalDataPayload - : Payload - where TOpticalDataError : IUserError - { - public Data.OpticalData? OpticalData { get; } - public IReadOnlyCollection? Errors { get; } - - protected OpticalDataPayload( - Data.OpticalData opticalData - ) - { - OpticalData = opticalData; - } - - protected OpticalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected OpticalDataPayload( - TOpticalDataError error - ) - : this(new[] { error }) - { - } - - protected OpticalDataPayload( - Data.OpticalData opticalData, - IReadOnlyCollection errors - ) - { - OpticalData = opticalData; - Errors = errors; - } - - protected OpticalDataPayload( - Data.OpticalData opticalData, - TOpticalDataError error - ) - : this( - opticalData, - new[] { error } - ) - { - } - } -} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 3264abb1..085a0059 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -1,45 +1,107 @@ using System; -using System.Linq; 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.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class OpticalDataQueries +: DataQueriesBase { - [ExtendObjectType(nameof(Query))] - public sealed class OpticalDataQueries + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllOpticalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) { - [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] - [UseSorting] - public IQueryable GetAllOpticalData( - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.OpticalData; - } + return GetAllDataAsync( + databaseContext => databaseContext.OpticalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingOpticalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.OpticalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } - public Task GetOpticalDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - OpticalDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + [UseFiltering] + public Task HasOpticalDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.OpticalData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetOpticalDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.OpticalData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs deleted file mode 100644 index b286dfa8..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.OpticalDataX -{ - public sealed class OpticalDataResolvers - { - public Task GetGetHttpsResources( - [Parent] Data.OpticalData opticalData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(opticalData.Id, cancellationToken); - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.OpticalData opticalData - ) - { - return new GetHttpsResourceTree(opticalData); - } - - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } - } -} \ 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..f90b2c7f --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs index da010b74..5adb469b 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs @@ -1,28 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; using HotChocolate.Types; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataType + : DataTypeBase { - 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); } -} +} \ No newline at end of file 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/Payload.cs b/backend/src/GraphQl/Payload.cs index 1220153d..86ca2954 100644 --- a/backend/src/GraphQl/Payload.cs +++ b/backend/src/GraphQl/Payload.cs @@ -1,6 +1,11 @@ -namespace Database.GraphQl +using System.Collections.Generic; + +namespace Database.GraphQl; + +public abstract record Payload { - 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 diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs deleted file mode 100644 index f41f478d..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.PhotovoltaicDataX -{ - public sealed class CreatePhotovoltaicDataError - : GraphQl.UserErrorBase - { - public CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs deleted file mode 100644 index 7dfe4393..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Database.GraphQl.PhotovoltaicDataX -{ - public enum CreatePhotovoltaicDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs deleted file mode 100644 index 6ff46c5d..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Types; - -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, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource - ); -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs new file mode 100644 index 00000000..dc8398e4 --- /dev/null +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -0,0 +1,173 @@ +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 Database.GraphQl.DataX; +using Database.GraphQl.Scalars; +using Database.Services; +using HotChocolate; +using HotChocolate.Authorization; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed record CreatePhotovoltaicDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTimeOffset CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource +) : IValidateCreateInput +{ + public PhotovoltaicData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new PhotovoltaicData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public 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 +} + +public sealed record CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreatePhotovoltaicDataPayload( + PhotovoltaicData? PhotovoltaicData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreatePhotovoltaicDataMutation +: CreateDataMutationBase +{ + // [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); + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreatePhotovoltaicDataAsync( + CreatePhotovoltaicDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + IClock clock, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreatePhotovoltaicDataErrorCode.UNAUTHENTICATED, + CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreatePhotovoltaicDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATABASE, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var photovoltaicData = input.ToDomainModel( + currentUserOrInstitution.CurrentUser?.Uuid, + dataFormat.Extension + ); + context.PhotovoltaicData.Add(photovoltaicData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + photovoltaicData, + CreatePhotovoltaicDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(photovoltaicData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(photovoltaicData, null); + } +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs deleted file mode 100644 index d5d0cad8..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Database.GraphQl.PhotovoltaicDataX -{ - public sealed class CreatePhotovoltaicDataPayload - : PhotovoltaicDataPayload - { - public CreatePhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData - ) - : base(photovoltaicData) - { - } - - public CreatePhotovoltaicDataPayload( - CreatePhotovoltaicDataError error - ) - : base(error) - { - } - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs deleted file mode 100644 index 542566fa..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GreenDonut; -using Database.GraphQl.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.PhotovoltaicDataX -{ - public sealed class PhotovoltaicDataByIdDataLoader - : EntityByIdDataLoader - { - public PhotovoltaicDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.PhotovoltaicData - ) - { - } - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index ff32fa41..bf535430 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -1,17 +1,37 @@ +using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; 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.Name(nameof(PhotovoltaicDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); + // DataFilterTypeBase.Configure + 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) + .Type>(); + descriptor.Field(_ => _.Warnings); } -} +} \ 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/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs deleted file mode 100644 index a048f9ce..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Linq; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Extensions; -using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; -using HotChocolate.Types; - -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( - CreatePhotovoltaicDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - [ScopedService] 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, - // 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(), - 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 - ) - ).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); - } - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs deleted file mode 100644 index aca2b591..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.PhotovoltaicDataX -{ - public abstract class PhotovoltaicDataPayload - : Payload - where TPhotovoltaicDataError : IUserError - { - public Data.PhotovoltaicData? PhotovoltaicData { get; } - public IReadOnlyCollection? Errors { get; } - - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData - ) - { - PhotovoltaicData = photovoltaicData; - } - - protected PhotovoltaicDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected PhotovoltaicDataPayload( - TPhotovoltaicDataError error - ) - : this(new[] { error }) - { - } - - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData, - IReadOnlyCollection errors - ) - { - PhotovoltaicData = photovoltaicData; - Errors = errors; - } - - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData, - TPhotovoltaicDataError error - ) - : this( - photovoltaicData, - new[] { error } - ) - { - } - } -} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index b2f1bcec..6688e878 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -1,45 +1,107 @@ using System; -using System.Linq; 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.Resolvers; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class PhotovoltaicDataQueries +: DataQueriesBase { - [ExtendObjectType(nameof(Query))] - public sealed class PhotovoltaicDataQueries + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPhotovoltaicDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) { - [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] - [UseSorting] - public IQueryable GetAllPhotovoltaicData( - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.PhotovoltaicData; - } + return GetAllDataAsync( + databaseContext => databaseContext.PhotovoltaicData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + public Task> GetAllPendingPhotovoltaicDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + databaseContext => databaseContext.PhotovoltaicData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + authorization, + cancellationToken + ); + } - public Task GetPhotovoltaicDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - PhotovoltaicDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + [UseFiltering] + public Task HasPhotovoltaicDataAsync( + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + databaseContext => databaseContext.PhotovoltaicData, + locale, + databaseContextFactory, + accessPolicyService, + resolverContext, + cancellationToken + ); + } + + public Task GetPhotovoltaicDataAsync( + Guid id, + [GraphQLType] string? locale, + IDbContextFactory databaseContextFactory, + AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + databaseContext => databaseContext.PhotovoltaicData, + databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, + cancellationToken + ); } -} +} \ 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 fc0f3779..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.PhotovoltaicDataX -{ - public sealed class PhotovoltaicDataResolvers - { - public Task GetGetHttpsResources( - [Parent] Data.PhotovoltaicData photovoltaicData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(photovoltaicData.Id, cancellationToken); - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.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/PhotovoltaicDataSortType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs new file mode 100644 index 00000000..2beaa4d7 --- /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(_ => _.Id); + } +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs index ed84ea3b..ee2a79e9 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs @@ -1,28 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; using HotChocolate.Types; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataType + : DataTypeBase { - 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); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationFilterType.cs b/backend/src/GraphQl/Publications/PublicationFilterType.cs new file mode 100644 index 00000000..98ba9228 --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationFilterType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.Publications; + +public sealed class PublicationFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.Field(_ => _.Exists).Ignore(); + descriptor.Name(nameof(PublicationFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + } +} diff --git a/backend/src/GraphQl/Publications/PublicationInput.cs b/backend/src/GraphQl/Publications/PublicationInput.cs new file mode 100644 index 00000000..dce40aae --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationInput.cs @@ -0,0 +1,32 @@ +using System; +using Database.Data; +using Database.GraphQl.Scalars; +using HotChocolate; + +namespace Database.GraphQl.Publications; + +public sealed record PublicationInput( + string? Title, + string? Abstract, + string? Section, + string[]? Authors, + [property: GraphQLType] string? Doi, + [property: GraphQLType] string? ArXiv, + [property: GraphQLType] string? Urn, + Uri? WebAddress +) +{ + 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/PublicationSortType.cs b/backend/src/GraphQl/Publications/PublicationSortType.cs new file mode 100644 index 00000000..1b1f8d04 --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationSortType.cs @@ -0,0 +1,15 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.Publications; + +public sealed class PublicationSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.Field(_ => _.Exists).Ignore(); + } +} diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs new file mode 100644 index 00000000..c103a128 --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -0,0 +1,15 @@ +using Database.Data; +using HotChocolate.Types; + +namespace Database.GraphQl.Publications; + +public sealed class PublicationType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + descriptor.Field(p => p.Exists).Ignore(); + } +} \ 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/References/ReferenceFilterType.cs b/backend/src/GraphQl/References/ReferenceFilterType.cs new file mode 100644 index 00000000..661ae745 --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceFilterType.cs @@ -0,0 +1,19 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.References; + +public sealed class ReferenceFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(ReferenceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.Standard); + descriptor.Field(_ => _.Publication); + } +} diff --git a/backend/src/GraphQl/References/ReferenceInput.cs b/backend/src/GraphQl/References/ReferenceInput.cs new file mode 100644 index 00000000..150c4544 --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceInput.cs @@ -0,0 +1,35 @@ +using System; +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 +) +{ + 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/ReferenceSortType.cs b/backend/src/GraphQl/References/ReferenceSortType.cs new file mode 100644 index 00000000..2c613290 --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.References; + +public sealed class ReferenceSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(_ => _.Standard); + descriptor.Field(_ => _.Publication); + } +} diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs new file mode 100644 index 00000000..047dd47e --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -0,0 +1,13 @@ +using Database.Data; +using HotChocolate.Types; + +namespace Database.GraphQl.References; + +public sealed class ReferenceType + : UnionType +{ + protected override void Configure(IUnionTypeDescriptor descriptor) + { + descriptor.Name(nameof(IReference)[1..]); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs new file mode 100644 index 00000000..1e91b2b5 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Concurrent; +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.GraphQl.Extensions; +using Database.Services; +using GreenDonut.Data; +using HotChocolate.Data; +using HotChocolate.Authorization; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.Logging; + +namespace Database.GraphQl.ResponseApprovals; + +[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 Log +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Creating response approval for data of type {Type} with ID {Id}." + )] + public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); +} + +public sealed record CreateResponseApprovalsPayload( + IReadOnlyCollection? Data, + 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> +{ + //[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] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task CreateResponseApprovalsAsync( + IResolverContext resolverContext, + ApplicationDbContext context, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ILogger logger, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateResponseApprovalsErrorCode.UNAUTHENTICATED, + CreateResponseApprovalsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var dataSets = new ConcurrentBag(); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + context.GetAllDataAsync(_ => _.Approval == null, resolverContext.GetQueryContext()), + cancellationToken, + async (data, cancellationToken) => + { + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + 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}", + [] + ) + ); + } + } + ); + await context.SaveChangesAsync(cancellationToken); + if (!errors.IsEmpty) + { + return NewPayload(dataSets, errors); + } + 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..f29f5b4d --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -0,0 +1,34 @@ +using Database.Data; +using Database.GraphQl.Entities; +using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.ResponseApprovals; + +public abstract class ResponseApprovalFilterType +: AuditableEntityFilterType +{ + protected override void Configure( + 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); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor + .Field(_ => _.Resources) + .Type>(); + descriptor.Field(_ => _.Warnings); + } +} \ 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..90f188a2 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Concurrent; +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.GraphQl.Extensions; +using Database.Services; +using GreenDonut.Data; +using HotChocolate.Data; +using HotChocolate.Authorization; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.Logging; + +namespace Database.GraphQl.ResponseApprovals; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateResponseApprovalsErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATABASE, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public static partial class Log +{ + [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 record UpdateResponseApprovalsPayload( + IReadOnlyCollection? Data, + 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> +{ + 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] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async Task UpdateResponseApprovalsAsync( + IResolverContext resolverContext, + ApplicationDbContext context, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ILogger logger, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, + UpdateResponseApprovalsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var dataSets = new ConcurrentBag(); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + context.GetAllDataAsync(_ => _.Approval != null, resolverContext.GetQueryContext()), + 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( + 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}", + [] + ) + ); + } + } + ); + await context.SaveChangesAsync(cancellationToken); + if (!errors.IsEmpty) + { + return NewPayload(dataSets, errors); + } + return NewPayload(dataSets, null); + } +} diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 5075fc5c..fbb03124 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -1,13 +1,32 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; +using Database.Utilities; -namespace Database.GraphQl +namespace Database.GraphQl; + +public interface IValidateGetHttpsResourceInput { - public record RootGetHttpsResourceInput( - string Description, - string HashValue, - Guid DataFormatId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod - ); + public Guid DataFormatId { get; } + public IReadOnlyList ArchivedFilesMetaInformation { get; } } + +public sealed record RootGetHttpsResourceInput( + string Description, + Guid DataFormatId, + IReadOnlyList ArchivedFilesMetaInformation +) +: IValidateGetHttpsResourceInput +{ + 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/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 new file mode 100644 index 00000000..aff2a176 --- /dev/null +++ b/backend/src/GraphQl/Scalars/LocaleType.cs @@ -0,0 +1,45 @@ +using System.Text.RegularExpressions; +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.Scalars; + +public sealed 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]+)?$"; + + public LocaleType() + : this( + nameof(LocaleType)[..^"Type".Length], + """ + [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). + """ + ) + { + } +} \ No newline at end of file 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/Standards/StandardInput.cs b/backend/src/GraphQl/Standards/StandardInput.cs new file mode 100644 index 00000000..4eed09ee --- /dev/null +++ b/backend/src/GraphQl/Standards/StandardInput.cs @@ -0,0 +1,36 @@ +using System; +using Database.Data; +using Database.Enumerations; +using Database.GraphQl.Numerations; + +namespace Database.GraphQl.Standards; + +public sealed record StandardInput( + string? Title, + string? Abstract, + string? Section, + int? Year, + NumerationInput Numeration, + Standardizer[] Standardizers, + Uri? Locator +) +{ + 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 new file mode 100644 index 00000000..8865bf78 --- /dev/null +++ b/backend/src/GraphQl/Standards/StandardType.cs @@ -0,0 +1,14 @@ +using Database.Data; +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/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 891040ea..fa131d04 100644 --- a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs +++ b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs @@ -1,11 +1,22 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed record ToTreeVertexAppliedConversionMethodInput( + Guid MethodId, + IReadOnlyList Arguments, + string SourceName +) { - public 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/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/GraphQl/UserErrorBase.cs b/backend/src/GraphQl/UserErrorBase.cs index 596bd63c..49fc6eba 100644 --- a/backend/src/GraphQl/UserErrorBase.cs +++ b/backend/src/GraphQl/UserErrorBase.cs @@ -1,25 +1,12 @@ +using System; using System.Collections.Generic; -using Enum = System.Enum; -namespace Database.GraphQl -{ - public abstract class UserErrorBase - : IUserError - where TUserErrorCode : struct, Enum - { - public TUserErrorCode Code { get; } - public string Message { get; } - public IReadOnlyList Path { get; } +namespace Database.GraphQl; - protected UserErrorBase( - TUserErrorCode code, - string message, - IReadOnlyList path - ) - { - Code = code; - Message = message; - Path = path; - } - } -} +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/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/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs new file mode 100644 index 00000000..b331edad --- /dev/null +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -0,0 +1,43 @@ +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using HotChocolate.Authorization; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using static Database.ApiRequests.GetUserInfo; + +namespace Database.GraphQl.Users; + +[ExtendObjectType(nameof(Query))] +public sealed class UserQueries +{ + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentUserAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrApplicationAsync( + (user, _) => Task.FromResult(user), + (application, _) => Task.FromResult(null), + cancellationToken + ); + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentUserInfoAsync( + GetUserInfo getUserInfo, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GraphQlRequestHelper.TransformExceptionsAsync( + () => getUserInfo.Do( + cancellationToken + ), + resolverContext, + getUserInfo.GetEndpoint + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/VerificationCodeQueries.cs b/backend/src/GraphQl/VerificationCodeQueries.cs new file mode 100644 index 00000000..17d8b334 --- /dev/null +++ b/backend/src/GraphQl/VerificationCodeQueries.cs @@ -0,0 +1,15 @@ +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl; + +[ExtendObjectType(nameof(Query))] +public sealed class VerificationCodeQueries +{ + public string GetVerificationCode( + AppSettings appSettings + ) + { + return appSettings.VerificationCode; + } +} \ No newline at end of file diff --git a/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs new file mode 100644 index 00000000..3165f2ec --- /dev/null +++ b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs @@ -0,0 +1,230 @@ +using System; +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; + +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( + IClock clock, + 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, IClock clock) + { + // 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 = clock.GetUtcNow().ToDateTimeOffset(); + 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, clock); + } + catch (Exception exception) + { + logger.FailedToCreateCertificate(distinguishedName, exception); + return null; + } + } + + public static X509Certificate2 CreateEncryptionCertificate(string distinguishedName, IClock clock) + { + // 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 = clock.GetUtcNow().ToDateTimeOffset(); + 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, clock); + } + 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 = clock.GetUtcNow().ToDateTimeOffset(); + foreach (var certificate in certificates) + { + // Use `RefreshTokenLifetime` 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/Json/JsonDocumentSettings.cs b/backend/src/Json/JsonDocumentSettings.cs new file mode 100644 index 00000000..7f297ebb --- /dev/null +++ b/backend/src/Json/JsonDocumentSettings.cs @@ -0,0 +1,20 @@ +using System.Text.Json; + +namespace Database.Json; + +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/Json/JsonSerializerSettings.cs b/backend/src/Json/JsonSerializerSettings.cs new file mode 100644 index 00000000..0d87945d --- /dev/null +++ b/backend/src/Json/JsonSerializerSettings.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using NodaTime; +using NodaTime.Serialization.SystemTextJson; + +namespace Database.Json; + +/// +/// Options for JSON serialization. +/// +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); + + public static readonly JsonSerializerOptions GraphQl = + new(s_common) + { + // 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) + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + }; + + public static readonly JsonSerializerOptions BedJson = + new(s_common) + { + // 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/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/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 new file mode 100644 index 00000000..149529f5 --- /dev/null +++ b/backend/src/Json/ReferenceJsonConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Database.Data; + +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 +{ + 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/Json/ToCamelCaseJsonNamingPolicy.cs b/backend/src/Json/ToCamelCaseJsonNamingPolicy.cs new file mode 100644 index 00000000..088d8457 --- /dev/null +++ b/backend/src/Json/ToCamelCaseJsonNamingPolicy.cs @@ -0,0 +1,14 @@ +using System.Text.Json; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.Json; + +/// +/// 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(); +} \ No newline at end of file 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/Logging/GraphQlErrorTagProvider.cs b/backend/src/Logging/GraphQlErrorTagProvider.cs new file mode 100644 index 00000000..f64529a9 --- /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..9398a1f9 --- /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/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs new file mode 100644 index 00000000..cba4bedb --- /dev/null +++ b/backend/src/Methods/IMethod.cs @@ -0,0 +1,8 @@ +using System.Text.Json; + +namespace Database.Methods; + +public interface IMethod +{ + 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 new file mode 100644 index 00000000..aaa2ea41 --- /dev/null +++ b/backend/src/Methods/MethodBase.cs @@ -0,0 +1,21 @@ +using System.Text.Json; +using Database.Json; + +namespace Database.Methods; + +public abstract class MethodBase + : IMethod +{ + public JsonElement Calculate(JsonElement input) + { + 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) + ); + return document.RootElement.Clone(); + } + + public abstract TOutput Calculate(TInput input); +} \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegral/CalculationStandard.cs b/backend/src/Methods/SpectralToIntegral/CalculationStandard.cs new file mode 100644 index 00000000..a2bf6663 --- /dev/null +++ b/backend/src/Methods/SpectralToIntegral/CalculationStandard.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Methods.SpectralToIntegral; + +[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/SpectralToIntegral/SpectralToIntegralInput.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs new file mode 100644 index 00000000..9642793b --- /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( + IReadOnlyList 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/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs new file mode 100644 index 00000000..367dc4ff --- /dev/null +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace Database.Methods.SpectralToIntegral; + +public sealed class SpectralToIntegralMethod + : MethodBase +{ + public static readonly Guid Id = Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); + + public ImmutableArray<(int wavelength, double weight, double deltaWavelength)> En410VisibleWavelengthsWeights => [ + (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 ImmutableArray<(int wavelength, double weight, double deltaWavelength)> En410SolarWavelengthsWeights => [ + (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) + ]; + + public ImmutableArray<(int wavelength, double weight, double deltaWavelength)> Iso9050SolarWavelengthsWeights => [ + (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 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(firstDataItem.DataPoints, En410VisibleWavelengthsWeights), + En410Solar: Calculate(firstDataItem.DataPoints, En410SolarWavelengthsWeights), + Iso9050Solar: Calculate(firstDataItem.DataPoints, Iso9050SolarWavelengthsWeights) + ); + } + + public double Calculate( + IReadOnlyList spectralDataPoints, + ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights + ) + { + if (spectralDataPoints is null || spectralDataPoints.Count is 0) + { + throw new ArgumentException("The list `spectralDataPoints` is empty."); + } + var sortedSpectralDataPoints = + spectralDataPoints + .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) + .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) + .ToList() + .AsReadOnly(); + var numerator = 0.0D; + var denominator = 0.0D; + for (int i = 0; i < (wavelengthsWeights.Length - 1); i++) + { + 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) + { + 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`.\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 + + spectralDataPointWavelengthAbove.Results.Transmittance + ) / 2; + 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; + } + + // 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; + } +} \ 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..7383ba8b --- /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( + IReadOnlyList Data +); + +public sealed record TransmittanceSumData( + 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..9974957b --- /dev/null +++ b/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs @@ -0,0 +1,20 @@ +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; + // TODO What to do when there is more than 1 item? + foreach (var dataPoint in input.Data[0].DataPoints) + { + transmittanceSum += dataPoint.Results.Transmittance; + } + return transmittanceSum; + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs b/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs index f1edce5e..323c4cf8 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; @@ -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") diff --git a/backend/src/Migrations/20211019125407_InitialCreate.cs b/backend/src/Migrations/20211019125407_InitialCreate.cs index 15bb1ece..6dd968b9 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.cs @@ -1,658 +1,661 @@ -using System; +using System; using System.Text.Json; using Database.Enumerations; 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.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", ",,"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs new file mode 100644 index 00000000..a560c1df --- /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..2878e2ea --- /dev/null +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -0,0 +1,234 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations; + +/// +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) + { + 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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs new file mode 100644 index 00000000..88112da6 --- /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..1acd1293 --- /dev/null +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs @@ -0,0 +1,37 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs b/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs new file mode 100644 index 00000000..fa6c6a5d --- /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..11b89873 --- /dev/null +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -0,0 +1,181 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations; + +/// +public partial class OpenIddict : Migration +{ + 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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs b/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs new file mode 100644 index 00000000..0a10ee95 --- /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..3c50ce01 --- /dev/null +++ b/backend/src/Migrations/20230427133946_CreateUserTable.cs @@ -0,0 +1,38 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs new file mode 100644 index 00000000..daa5104f --- /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..a09ad391 --- /dev/null +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs @@ -0,0 +1,79 @@ +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: ""); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs new file mode 100644 index 00000000..3cc819ed --- /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..5b36e5d4 --- /dev/null +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs @@ -0,0 +1,848 @@ +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", ",,"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs new file mode 100644 index 00000000..53133924 --- /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..b344458c --- /dev/null +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs @@ -0,0 +1,186 @@ +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); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs b/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs new file mode 100644 index 00000000..400961f5 --- /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..bfb91261 --- /dev/null +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -0,0 +1,197 @@ +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: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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs new file mode 100644 index 00000000..0ab91fb5 --- /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..8d11504d --- /dev/null +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs @@ -0,0 +1,64 @@ +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/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs new file mode 100644 index 00000000..3d4d502c --- /dev/null +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs @@ -0,0 +1,2105 @@ +// +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", "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") + .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.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 + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs new file mode 100644 index 00000000..c18b2942 --- /dev/null +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs @@ -0,0 +1,64 @@ +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.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: "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); + } + + /// + 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: "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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs new file mode 100644 index 00000000..e9cb40e5 --- /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..41634a87 --- /dev/null +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs @@ -0,0 +1,37 @@ +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); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs new file mode 100644 index 00000000..fc8bf85a --- /dev/null +++ b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs @@ -0,0 +1,2309 @@ +// +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("20250331141152_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.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.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/20250331141152_AddAccessRights.cs b/backend/src/Migrations/20250331141152_AddAccessRights.cs new file mode 100644 index 00000000..47b2b61e --- /dev/null +++ b/backend/src/Migrations/20250331141152_AddAccessRights.cs @@ -0,0 +1,341 @@ +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: "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"); + } +} \ No newline at end of file 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..035933b2 --- /dev/null +++ b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs @@ -0,0 +1,199 @@ +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"); + } +} \ No newline at end of file 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..887c02d3 --- /dev/null +++ b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs @@ -0,0 +1,51 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs new file mode 100644 index 00000000..8ac56ea6 --- /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..c7469b44 --- /dev/null +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs @@ -0,0 +1,1102 @@ +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/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs new file mode 100644 index 00000000..774d025a --- /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..71cb72ac --- /dev/null +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs @@ -0,0 +1,77 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs new file mode 100644 index 00000000..7d9164ea --- /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..e7253c78 --- /dev/null +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs @@ -0,0 +1,41 @@ +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); + } +} \ 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..0a3267c8 --- /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..a21b43b2 --- /dev/null +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs @@ -0,0 +1,29 @@ +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"); + } +} \ 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..f47fe2ff --- /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..40be0f47 --- /dev/null +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs @@ -0,0 +1,148 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs new file mode 100644 index 00000000..be0870f9 --- /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..d3f78b42 --- /dev/null +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs @@ -0,0 +1,137 @@ +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"); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs new file mode 100644 index 00000000..9bb42b37 --- /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..819ba2af --- /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"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs new file mode 100644 index 00000000..a914cfe7 --- /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..ffa54ac3 --- /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/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs new file mode 100644 index 00000000..1a1e99e5 --- /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..da38a0f2 --- /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"); + } + } +} \ 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..71088e51 --- /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..e83a625f --- /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/20251030170839_AddPublishingStateToDataSets.Designer.cs b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs new file mode 100644 index 00000000..734a9fed --- /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..837bfa25 --- /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/20251031125736_AddFileExtensionToResource.Designer.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs new file mode 100644 index 00000000..f3bffeae --- /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..6e8bd06d --- /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"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs new file mode 100644 index 00000000..22f4aeef --- /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..3213579e --- /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"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs new file mode 100644 index 00000000..d3d91d7f --- /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..b0052c87 --- /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: System.Array.Empty()); + + migrationBuilder.AddColumn( + name: "Widths", + schema: "database", + table: "geometric_data", + type: "double precision[]", + nullable: false, + defaultValue: System.Array.Empty()); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Heights", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Widths", + schema: "database", + table: "geometric_data"); + } + } +} \ 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..f9012a16 --- /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/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/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..cb7891e4 --- /dev/null +++ b/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class RevampAccessPolicies : Migration + { + /// + [SuppressMessage("Performance", "CA1861")] + 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); + } + + /// + [SuppressMessage("Performance", "CA1861")] + 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/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/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/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/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs new file mode 100644 index 00000000..933d836c --- /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/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 355d6176..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; @@ -6,8 +6,11 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +#nullable disable + namespace Database.Migrations { [DbContext(typeof(ApplicationDbContext))] @@ -18,125 +21,257 @@ 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); - - modelBuilder.Entity("Database.Data.CalorimetricData", b => + .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("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); - b.Property("ComponentId") + b.Property("CalorimetricDataId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); - b.Property("CreatorId") - .HasColumnType("uuid"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("Description") - .HasColumnType("text"); + b.Property("GeometricDataId") + .HasColumnType("uuid"); - b.Property("GValues") - .IsRequired() - .HasColumnType("double precision[]"); + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); - b.Property("Locale") - .IsRequired() - .HasColumnType("text"); + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); - b.Property("Name") - .HasColumnType("text"); + b.Property("OpticalDataId") + .HasColumnType("uuid"); - b.Property("UValues") - .IsRequired() - .HasColumnType("double precision[]"); + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); - b.Property("Warnings") - .IsRequired() - .HasColumnType("text[]"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("xmin") + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); b.HasKey("Id"); - b.ToTable("calorimetric_data"); + 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.GetHttpsResource", b => + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); - b.Property("CalorimetricDataId") + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") .HasColumnType("uuid"); - b.Property("DataFormatId") + b.Property("InstitutionId") .HasColumnType("uuid"); - b.Property("Description") - .IsRequired() - .HasColumnType("text"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("HashValue") + 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("HygrothermalDataId") - .HasColumnType("uuid"); - - b.Property("OpticalDataId") - .HasColumnType("uuid"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("ParentId") + b.Property("DataAccessPolicyId") .HasColumnType("uuid"); - b.Property("PhotovoltaicDataId") - .HasColumnType("uuid"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("xmin") + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); b.HasKey("Id"); - b.HasIndex("CalorimetricDataId"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); - b.HasIndex("HygrothermalDataId"); + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); - b.HasIndex("OpticalDataId"); + b.ToTable("open_id_connect_application_access_policy", "database"); + }); - b.HasIndex("ParentId"); + 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.HasIndex("PhotovoltaicDataId"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); - b.ToTable("get_https_resource"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); }); - modelBuilder.Entity("Database.Data.HygrothermalData", b => + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -144,6 +279,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + b.Property("Locale") .IsRequired() .HasColumnType("text"); @@ -151,36 +290,66 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Warnings") + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") .IsRequired() - .HasColumnType("text[]"); + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); - b.Property("xmin") + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); b.HasKey("Id"); - b.ToTable("hygrothermal_data"); + 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.OpticalData", b => + modelBuilder.Entity("Database.Data.GeometricData", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("ColorRenderingIndices") - .IsRequired() - .HasColumnType("double precision[]"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -188,7 +357,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); - b.Property("InfraredEmittances") + b.PrimitiveCollection("Heights") .IsRequired() .HasColumnType("double precision[]"); @@ -199,48 +368,183 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("NearnormalHemisphericalSolarReflectances") - .IsRequired() - .HasColumnType("double precision[]"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); - b.Property("NearnormalHemisphericalSolarTransmittances") + b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); - b.Property("NearnormalHemisphericalVisibleReflectances") + 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("double precision[]"); + .HasColumnType("text[]"); - b.Property("NearnormalHemisphericalVisibleTransmittances") + b.PrimitiveCollection("Widths") .IsRequired() .HasColumnType("double precision[]"); - b.Property("Warnings") + 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[]"); + .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("xmin") + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); b.HasKey("Id"); - b.ToTable("optical_data"); + 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.PhotovoltaicData", b => + modelBuilder.Entity("Database.Data.HygrothermalData", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -255,112 +559,1639 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Warnings") - .IsRequired() - .HasColumnType("text[]"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); - b.Property("xmin") + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); b.HasKey("Id"); - b.ToTable("photovoltaic_data"); - }); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); - modelBuilder.Entity("Database.Data.CalorimetricData", b => - { - b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + b.ToTable("hygrothermal_data", "database", t => { - b1.Property("CalorimetricDataId") - .HasColumnType("uuid"); + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); - b1.Property("MethodId") - .HasColumnType("uuid"); + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); - b1.HasKey("CalorimetricDataId"); + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); - b1.ToTable("calorimetric_data"); + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); - b1.WithOwner() - .HasForeignKey("CalorimetricDataId"); + 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\"();"); + }); - b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => - { - b2.Property("AppliedMethodCalorimetricDataId") - .HasColumnType("uuid"); + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); - b2.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + b.Property("ComponentId") + .HasColumnType("uuid"); - b2.Property("Name") - .IsRequired() - .HasColumnType("text"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b2.Property("Value") - .IsRequired() - .HasColumnType("jsonb"); + 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_Arguments"); + 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("AppliedMethodCalorimetricDataId"); + .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("AppliedMethodCalorimetricDataId") + b2.Property("AppliedMethodHygrothermalDataId") .HasColumnType("uuid"); b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() .HasColumnType("text"); - b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); - b2.ToTable("calorimetric_data_Sources"); + b2.ToTable("hygrothermal_data_Sources", "database"); b2.WithOwner() - .HasForeignKey("AppliedMethodCalorimetricDataId"); + .HasForeignKey("AppliedMethodHygrothermalDataId"); b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => { - b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); - b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); - b3.ToTable("calorimetric_data_Sources"); + b3.ToTable("hygrothermal_data_Sources", "database"); b3.WithOwner() - .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); }); b2.Navigation("Value") @@ -372,15 +2203,56 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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("CalorimetricDataId") + b1.Property("HygrothermalDataId") .HasColumnType("uuid"); b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -389,11 +2261,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"); @@ -401,225 +2273,254 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); - b1.HasKey("CalorimetricDataId", "Id"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); - b1.ToTable("calorimetric_data_Approvals"); + b1.ToTable("hygrothermal_data_Approvals", "database"); b1.WithOwner() - .HasForeignKey("CalorimetricDataId"); - }); + .HasForeignKey("HygrothermalDataId"); - b.Navigation("AppliedMethod") - .IsRequired(); + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); - b.Navigation("Approvals"); - }); + b2.Property("DataApprovalId") + .HasColumnType("integer"); - modelBuilder.Entity("Database.Data.GetHttpsResource", b => - { - b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") - .WithMany("Resources") - .HasForeignKey("CalorimetricDataId"); + b2.Property("Exists") + .HasColumnType("boolean"); - b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") - .WithMany("Resources") - .HasForeignKey("HygrothermalDataId"); + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); - b.HasOne("Database.Data.OpticalData", "OpticalData") - .WithMany("Resources") - .HasForeignKey("OpticalDataId"); + b2.ToTable("hygrothermal_data_Approvals", "database"); - b.HasOne("Database.Data.GetHttpsResource", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId"); + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); - b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") - .WithMany("Resources") - .HasForeignKey("PhotovoltaicDataId"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); - b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => - { - b1.Property("GetHttpsResourceId") - .HasColumnType("uuid"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + b3.Property("Abstract") + .HasColumnType("text"); - b1.Property("DataFormatId") - .HasColumnType("uuid"); + b3.Property("ArXiv") + .HasColumnType("text"); - b1.Property("Path") - .IsRequired() - .HasColumnType("text[]"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b1.HasKey("GetHttpsResourceId", "Id"); + b3.Property("Doi") + .HasColumnType("text"); - b1.ToTable("FileMetaInformation"); + b3.Property("Exists") + .HasColumnType("boolean"); - b1.WithOwner() - .HasForeignKey("GetHttpsResourceId"); - }); + b3.Property("Section") + .HasColumnType("text"); - b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => - { - b1.Property("GetHttpsResourceId") - .HasColumnType("uuid"); + b3.Property("Title") + .HasColumnType("text"); - b1.Property("MethodId") - .HasColumnType("uuid"); + b3.Property("Urn") + .HasColumnType("text"); - b1.Property("SourceName") - .IsRequired() - .HasColumnType("text"); + b3.Property("WebAddress") + .HasColumnType("text"); - b1.HasKey("GetHttpsResourceId"); + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); - b1.ToTable("get_https_resource"); + b3.ToTable("hygrothermal_data_Approvals", "database"); - b1.WithOwner() - .HasForeignKey("GetHttpsResourceId"); + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); - b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => - { - b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); - b2.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Name") - .IsRequired() - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Value") - .IsRequired() - .HasColumnType("jsonb"); + b3.Property("Locator") + .HasColumnType("text"); - b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + b3.Property("Section") + .HasColumnType("text"); - b2.ToTable("get_https_resource_Arguments"); + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); - b2.WithOwner() - .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); - }); + b3.Property("Title") + .HasColumnType("text"); - b1.Navigation("Arguments"); - }); + b3.Property("Year") + .HasColumnType("integer"); - b.Navigation("AppliedConversionMethod"); + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); - b.Navigation("ArchivedFilesMetaInformation"); + b3.ToTable("hygrothermal_data_Approvals", "database"); - b.Navigation("CalorimetricData"); + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); - b.Navigation("HygrothermalData"); + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); - b.Navigation("OpticalData"); + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); - b.Navigation("Parent"); + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); - b.Navigation("PhotovoltaicData"); + 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.HygrothermalData", b => + modelBuilder.Entity("Database.Data.LifeCycleData", b => { b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => { - b1.Property("HygrothermalDataId") + b1.Property("LifeCycleDataId") .HasColumnType("uuid"); b1.Property("MethodId") .HasColumnType("uuid"); - b1.HasKey("HygrothermalDataId"); + b1.HasKey("LifeCycleDataId"); - b1.ToTable("hygrothermal_data"); + b1.ToTable("lifeCycle_data", "database"); b1.WithOwner() - .HasForeignKey("HygrothermalDataId"); + .HasForeignKey("LifeCycleDataId"); b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => { - b2.Property("AppliedMethodHygrothermalDataId") + b2.Property("AppliedMethodLifeCycleDataId") .HasColumnType("uuid"); b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); - b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); - b2.ToTable("hygrothermal_data_Arguments"); + b2.ToTable("lifeCycle_data_Arguments", "database"); b2.WithOwner() - .HasForeignKey("AppliedMethodHygrothermalDataId"); + .HasForeignKey("AppliedMethodLifeCycleDataId"); }); b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => { - b2.Property("AppliedMethodHygrothermalDataId") + b2.Property("AppliedMethodLifeCycleDataId") .HasColumnType("uuid"); b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() .HasColumnType("text"); - b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); - b2.ToTable("hygrothermal_data_Sources"); + b2.ToTable("lifeCycle_data_Sources", "database"); b2.WithOwner() - .HasForeignKey("AppliedMethodHygrothermalDataId"); + .HasForeignKey("AppliedMethodLifeCycleDataId"); b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => { - b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); - b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); - b3.ToTable("hygrothermal_data_Sources"); + b3.ToTable("lifeCycle_data_Sources", "database"); b3.WithOwner() - .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); }); b2.Navigation("Value") @@ -631,15 +2532,56 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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("HygrothermalDataId") + b1.Property("LifeCycleDataId") .HasColumnType("uuid"); b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -648,11 +2590,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"); @@ -660,25 +2602,202 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); - b1.HasKey("HygrothermalDataId", "Id"); + 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"); - b1.ToTable("hygrothermal_data_Approvals"); + 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.WithOwner() - .HasForeignKey("HygrothermalDataId"); + 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") @@ -689,7 +2808,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("OpticalDataId"); - b1.ToTable("optical_data"); + b1.ToTable("optical_data", "database"); b1.WithOwner() .HasForeignKey("OpticalDataId"); @@ -701,20 +2820,20 @@ 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() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodOpticalDataId", "Id"); - b2.ToTable("optical_data_Arguments"); + b2.ToTable("optical_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodOpticalDataId"); @@ -727,8 +2846,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 +2856,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,25 +2867,23 @@ 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"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + b3.Property("DataTimestamp") + .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,28 +2898,41 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); - b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => { b1.Property("OpticalDataId") .HasColumnType("uuid"); - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + b1.Property("ApproverId") + .HasColumnType("uuid"); - b1.Property("AStar") - .HasColumnType("double precision"); + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); - b1.Property("BStar") - .HasColumnType("double precision"); + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); - b1.Property("LStar") - .HasColumnType("double precision"); + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); - b1.HasKey("OpticalDataId", "Id"); + 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("CielabColor"); + b1.ToTable("optical_data", "database"); b1.WithOwner() .HasForeignKey("OpticalDataId"); @@ -814,8 +2945,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"); @@ -824,11 +2956,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"); @@ -836,20 +2968,160 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); b1.HasKey("OpticalDataId", "Id"); - b1.ToTable("optical_data_Approvals"); + 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"); @@ -867,7 +3139,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("PhotovoltaicDataId"); - b1.ToTable("photovoltaic_data"); + b1.ToTable("photovoltaic_data", "database"); b1.WithOwner() .HasForeignKey("PhotovoltaicDataId"); @@ -879,20 +3151,20 @@ 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() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); - b2.ToTable("photovoltaic_data_Arguments"); + b2.ToTable("photovoltaic_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodPhotovoltaicDataId"); @@ -905,8 +3177,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 +3187,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,25 +3198,23 @@ 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"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + b3.Property("DataTimestamp") + .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"); @@ -958,6 +3229,46 @@ protected override void BuildModel(ModelBuilder modelBuilder) 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") @@ -965,8 +3276,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"); @@ -975,11 +3287,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"); @@ -987,25 +3299,207 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); b1.HasKey("PhotovoltaicDataId", "Id"); - b1.ToTable("photovoltaic_data_Approvals"); + 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"); }); @@ -1016,18 +3510,43 @@ 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"); }); + + 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/current b/backend/src/Migrations/current deleted file mode 100644 index d51fb881..00000000 --- a/backend/src/Migrations/current +++ /dev/null @@ -1 +0,0 @@ -20211019125407_InitialCreate diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql new file mode 100644 index 00000000..1da1ad46 --- /dev/null +++ b/backend/src/Migrations/migrate.sql @@ -0,0 +1,3618 @@ +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; + +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 ('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; + + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251010164110_UpdateCoatedSideEnumeration', '9.0.8'); + + END IF; +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; + +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; + +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; + UPDATE database.photovoltaic_data SET "PublishingState" = 'published'; + 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; + UPDATE database.optical_data SET "PublishingState" = 'published'; + 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; + UPDATE database.hygrothermal_data SET "PublishingState" = 'published'; + 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; + UPDATE database.geometric_data SET "PublishingState" = 'published'; + 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; + UPDATE database.calorimetric_data SET "PublishingState" = 'published'; + 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; + +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; + +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; + +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; + +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; + +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; + +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" = '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; + +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 ("Combinator") VALUES ('all'::database.logical_combinator); + + 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; + +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 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 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 database."data_access_policy" + ("GeometricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("HygrothermalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("LifeCycleDataId", "Combinator") + VALUES (NEW."Id", 'all') + 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; + 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 database."data_access_policy" + ("OpticalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("PhotovoltaicDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL DO NOTHING; + 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; + +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; + +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; + +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_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/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/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/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..2186dc7f --- /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" 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'); + +COMMIT; + 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/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql new file mode 100644 index 00000000..9630fa08 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql @@ -0,0 +1,66 @@ +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.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'); + +COMMIT; + 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/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/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/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/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/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..3ac8fa68 --- /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 ('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; + + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251010164110_UpdateCoatedSideEnumeration', '9.0.8'); + + 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/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..d739b8f3 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql @@ -0,0 +1,69 @@ +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_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql new file mode 100644 index 00000000..2d7abad2 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql @@ -0,0 +1,58 @@ +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; + UPDATE database.photovoltaic_data SET "PublishingState" = 'published'; + 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; + UPDATE database.optical_data SET "PublishingState" = 'published'; + 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; + UPDATE database.hygrothermal_data SET "PublishingState" = 'published'; + 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; + UPDATE database.geometric_data SET "PublishingState" = 'published'; + 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; + UPDATE database.calorimetric_data SET "PublishingState" = 'published'; + 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_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/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/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/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/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/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/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..efddc5bb --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql @@ -0,0 +1,102 @@ +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 ("Combinator") VALUES ('all'::database.logical_combinator); + + 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/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..32e187f0 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql @@ -0,0 +1,332 @@ +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 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 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 database."data_access_policy" + ("GeometricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("HygrothermalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("LifeCycleDataId", "Combinator") + VALUES (NEW."Id", 'all') + 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; + 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 database."data_access_policy" + ("OpticalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL DO NOTHING; + 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 database."data_access_policy" + ("PhotovoltaicDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL DO NOTHING; + 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_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/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; + 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/previous b/backend/src/Migrations/previous deleted file mode 100644 index 573541ac..00000000 --- a/backend/src/Migrations/previous +++ /dev/null @@ -1 +0,0 @@ -0 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'; 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; + 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; + 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; + 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; + diff --git a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql new file mode 100644 index 00000000..cadd91de --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql @@ -0,0 +1,58 @@ +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.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.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.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.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"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250331141152_AddAccessRights'; + +COMMIT; + 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; + 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; + 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/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; + 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; + 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..646866f5 --- /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 ('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; + + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration'; + + 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/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/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/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/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; + 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; + 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/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/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/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; + 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/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; + 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/Program.cs b/backend/src/Program.cs index 4e79c8ec..2f8fc4db 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -1,220 +1,297 @@ using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; using System.Threading.Tasks; +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; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using System.IO; using Serilog; +using Serilog.Enrichers.Span; using Serilog.Events; using Serilog.Formatting.Compact; -using Microsoft.AspNetCore.Hosting.StaticWebAssets; +using Serilog.Sinks.OpenTelemetry; + +namespace Database; + +public static partial class Log +{ + [LoggerMessage( + 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 + ); -namespace Database + [LoggerMessage( + Level = LogLevel.Error, + 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 + Exception exception + ); +} + +public sealed class Program { - public 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( + string[] commandLineArguments + ) { - public static async Task Main( - 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, openTelemetryHost); + try { - var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? throw new Exception("Unknown enrivornment."); - ConfigureBootstrapLogging(environment); - try + Serilog.Log.Information("Starting web host"); + // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication + var builder = CreateWebApplicationBuilder(commandLineArguments, openTelemetryHost); + 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"); - var host = CreateHostBuilder(commandLineArguments).Build(); - using var scope = host.Services.CreateScope(); - var services = scope.ServiceProvider; - var webHostEnvironment = services.GetRequiredService(); - if (webHostEnvironment.IsDevelopment()) + var lifetime = scope.ServiceProvider.GetRequiredService(); + if (!builder.Environment.IsEnvironment(TestEnvironment)) { - // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDbIfNotExists(services).ConfigureAwait(false); + 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 } - host.Run(); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly"); - return 1; - } - finally - { - Log.CloseAndFlush(); + if (builder.Environment.IsEnvironment(TestEnvironment)) + { + await CreateDatabaseAsync(scope.ServiceProvider, lifetime.ApplicationStopping); + } + await SeedDatabaseAsync(scope.ServiceProvider, lifetime.ApplicationStopping); + await AssertGnuPgKeyExistence(scope.ServiceProvider, lifetime.ApplicationStopping); } + // dotnet run -- schema export --output ./schema.graphql + return await application.RunWithGraphQLCommandsAsync(commandLineArguments); } - - private static void ConfigureBootstrapLogging( - string environment - ) + catch (OperationCanceledException exception) { - var configuration = new LoggerConfiguration() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information); - ConfigureLogging(configuration, environment); - Log.Logger = configuration.CreateBootstrapLogger(); + Serilog.Log.Information(exception, "[System] App execution cancelled cleanly."); + return 1; } - - private static void ConfigureLogging( - LoggerConfiguration configuration, - string environment - ) + catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { - configuration - .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithProperty("Environment", environment) - .WriteTo.Console() - .WriteTo.File( - formatter: new CompactJsonFormatter(), - path: "./logs/serilog.json", - fileSizeLimitBytes: 1073741824, // 1 GB - retainedFileCountLimit: 7, - rollingInterval: RollingInterval.Day, - rollOnFileSizeLimit: true - ); - if (environment != "production") - { - configuration.WriteTo.Debug(); - } + Serilog.Log.Fatal(exception, "Host terminated unexpectedly"); + return 1; } - - public static async Task CreateAndSeedDbIfNotExists( - IServiceProvider services - ) + finally { - 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."); - } + Serilog.Log.CloseAndFlush(); } + } - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host - public static IHostBuilder CreateHostBuilder( - string[] commandLineArguments - ) - { - return new HostBuilder() - .UseContentRoot( - Directory.GetCurrentDirectory() - ) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host#host-configuration - .ConfigureHostConfiguration(configuration => - { - configuration.AddEnvironmentVariables(prefix: "DOTNET_"); - configuration.AddCommandLine(commandLineArguments); - } - ) - // 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) => + private static void ConfigureBootstrapLogging( + string environment, + Uri openTelemetryHost + ) + { + var configuration = new LoggerConfiguration() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information); + ConfigureLogging(configuration, environment, openTelemetryHost); + Serilog.Log.Logger = configuration.CreateBootstrapLogger(); + } + + private static void ConfigureLogging( + LoggerConfiguration configuration, + 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(_ => { - loggingBuilder.Configure(options => + _.Endpoint = openTelemetryHost.AbsoluteUri; + _.Protocol = OtlpProtocol.Grpc; + _.OnBeginSuppressInstrumentation = + OpenTelemetry.SuppressInstrumentationScope.Begin; + _.ResourceAttributes = new Dictionary { - 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; + ["service.name"] = "backend", + }; }) - .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() + .WriteTo.File( + new CompactJsonFormatter(), + LogsPath, + fileSizeLimitBytes: 1073741824, // 1 GB + rollingInterval: RollingInterval.Day, + rollOnFileSizeLimit: true, + retainedFileCountLimit: 7 ); + if (environment != ProductionEnvironment) + { + configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); } + } - public static void ConfigureAppConfiguration( - IConfigurationBuilder configuration, - IHostEnvironment environment, - string[] commandLineArguments - ) + private static async Task EnsureDatabaseIsUpToDateAsync( + IServiceProvider services, + CancellationToken cancellationToken + ) + { + 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 CreateDatabaseAsync( + IServiceProvider services, + CancellationToken cancellationToken + ) + { + try + { + await using var databaseContext = + await services.GetRequiredService>() + .CreateDbContextAsync(cancellationToken); + await databaseContext.Database.EnsureCreatedAsync(cancellationToken); + } + catch (Exception exception) + { + var logger = services.GetRequiredService>(); + logger.FailedToCreateDatabase(exception); + } + } + + private static async Task SeedDatabaseAsync( + IServiceProvider services, + CancellationToken cancellationToken + ) + { + try + { + await DbSeeder.DoAsync(services, cancellationToken); + } + catch (Exception exception) { - configuration - .SetBasePath(environment.ContentRootPath) - .AddJsonFile( - "appsettings.json", - optional: false, - reloadOnChange: !environment.IsEnvironment("test") - ) - .AddJsonFile( - $"appsettings.{environment.EnvironmentName}.json", - optional: false, - reloadOnChange: !environment.IsEnvironment("test") - ) - .AddEnvironmentVariables() - .AddEnvironmentVariables(prefix: "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables - .AddCommandLine(commandLineArguments); + var logger = services.GetRequiredService>(); + logger.FailedToSeedDatabase(exception); } } + + private static Task AssertGnuPgKeyExistence( + IServiceProvider services, + CancellationToken cancellationToken + ) + { + return services + .GetRequiredService() + .AssertGnuPgKeyExistenceAsync(cancellationToken); + } + + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host + private static WebApplicationBuilder CreateWebApplicationBuilder( + string[] commandLineArguments, + Uri openTelemetryHost + ) + { + var builder = WebApplication.CreateBuilder( + new WebApplicationOptions + { + Args = commandLineArguments, + ContentRootPath = Directory.GetCurrentDirectory() // PlatformServices.Default.Application.ApplicationBasePath + } + ); + // 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(_ => + { + _.ValidateScopes = true; + _.ValidateOnBuild = true; + }); + // https://github.com/serilog/serilog-aspnetcore#instructions + builder.Host.UseSerilog((webHostBuilderContext, loggerConfiguration) => + { + ConfigureLogging( + loggerConfiguration, + webHostBuilderContext.HostingEnvironment.EnvironmentName, + openTelemetryHost + ); + loggerConfiguration + .ReadFrom.Configuration(webHostBuilderContext.Configuration); + }); + builder.WebHost.ConfigureKestrel(_ => + { + // 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; + } + + private static void ConfigureAppConfiguration( + ConfigurationManager configuration, + IHostEnvironment environment, + string[] commandLineArguments + ) + { + configuration.Sources.Clear(); + configuration + .SetBasePath(environment.ContentRootPath) + .AddJsonFile( + "appsettings.json", + false, + !environment.IsEnvironment(TestEnvironment) + ) + .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/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/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/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs new file mode 100644 index 00000000..70421590 --- /dev/null +++ b/backend/src/Services/AccessPolicyService.cs @@ -0,0 +1,158 @@ +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 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, + ILogger logger +) +{ + public async Task ApplyAsync( + Func> getData, + Func, Task<(IReadOnlyList Data, TResult Result)>> then, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TData : class, IData + { + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + var openIdConnectClientId = userService.GetOpenIdConnectApplicationClientId(); + 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 (currentApplication is not null) + { + institutionIds = [currentApplication.Owner.Uuid]; + } + logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); + var policedData = PoliceData(getData(databaseContext), currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); + var (postProcessedData, result) = await then(policedData); + await IncrementAccessCountsAsync( + postProcessedData, + openIdConnectClientId, + currentUser?.Uuid, + institutionIds, + databaseContext, + cancellationToken + ); + return result; + } + + internal static IQueryable PoliceData( + IQueryable data, + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId, + ApplicationDbContext databaseContext + ) + where TData : class, IData + { + return data.Where(_ => + ( + _.AccessPolicy == null + || _.AccessPolicy.IsAccessAllowed(userId, institutionIds, openIdConnectClientId) + ) + && + ( + !databaseContext.DataAccessPolicies.Any(_ => + _.DataId == null + && !_.IsAccessAllowed(userId, institutionIds, openIdConnectClientId) + ) + ) + ); + } + + private async Task IncrementAccessCountsAsync( + IReadOnlyList allData, + string? openIdConnectClientId, + Guid? userId, + Guid[]? institutionIds, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + where TData : class, IData + { + if (userId is null && institutionIds is null && openIdConnectClientId is null) + { + return; + } + var allDataIds = allData.Select(_ => _.Id).ToArray(); + if (userId is not null) + { + foreach (var userAccessPolicy in + await databaseContext.UserAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && _.UserId == userId + ) + .ToListAsync(cancellationToken) + ) + { + userAccessPolicy.IncrementAccessCount(clock); + } + } + if (institutionIds is not null) + { + foreach (var institutionAccessPolicy in + await databaseContext.InstitutionAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && institutionIds.Contains(_.InstitutionId) + ) + .ToListAsync(cancellationToken) + ) + { + institutionAccessPolicy.IncrementAccessCount(clock); + } + } + if (openIdConnectClientId is not null) + { + foreach (var applicationAccessPolicy in + await databaseContext.OpenIdConnectApplicationAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && _.ClientId == openIdConnectClientId + ) + .ToListAsync(cancellationToken) + ) + { + applicationAccessPolicy.IncrementAccessCount(clock); + } + } + await databaseContext.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs new file mode 100644 index 00000000..15e4cfc2 --- /dev/null +++ b/backend/src/Services/ApiRequestService.cs @@ -0,0 +1,220 @@ +using System; +using System.Net; +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; +using Database.Authentication; +using Database.Extensions; +using Database.Json; +using GraphQL; +using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; + +namespace Database.Services; + +/// +/// Service to request GraphQL and REST APIs. +/// +public sealed class ApiRequestService( + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + AppSettings appSettings +) +{ + public const string CustomHttpClient = "Custom"; + + public Task QueryGraphQlAsJson( + Uri url, + GraphQLRequest request, + CancellationToken cancellationToken + ) + { + return Query( + HttpMethod.Post, + url, + async httpResponseContent => + { + using var document = await JsonDocument.ParseAsync( + await httpResponseContent.ReadAsStreamAsync(), + JsonDocumentSettings.Strict + ); + return document.RootElement.Clone(); + }, + MakeGraphQlJsonHttpContent(request), + cancellationToken + ); + } + + public Task QueryGraphQlAsString( + Uri url, + GraphQLRequest request, + CancellationToken cancellationToken + ) + { + return Query( + HttpMethod.Post, + url, + httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), + MakeGraphQlJsonHttpContent(request), + cancellationToken + ); + } + + /// + /// Send GraphQL request to API and return response. + /// + public Task> QueryGraphQl( + Uri url, + GraphQLRequest request, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class + { + return Query>( + HttpMethod.Post, + url, + MakeGraphQlJsonHttpContent(request), + JsonSerializerSettings.GraphQl, + cancellationToken + ); + } + + public Task<(JsonElement Json, string? Sha256Hash)> PerformHttpGetRequest( + Uri url, + CancellationToken cancellationToken + ) + { + return Query( + HttpMethod.Get, + 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( + cryptoStream, + JsonDocumentSettings.Lax + ); + var sha256Hash = sha256.Hash is null ? null : Convert.ToHexString(sha256.Hash); + return (document.RootElement.Clone(), sha256Hash); + }, + null, + cancellationToken + ); + } + + public Task PerformHttpGetRequest( + Uri uri, + CancellationToken cancellationToken + ) + where TResponse : class + { + return Query( + HttpMethod.Get, + uri, + null, + JsonSerializerSettings.Rest, + cancellationToken + ); + } + + private async Task Query( + HttpMethod httpMethod, + Uri url, + HttpContent? httpRequestContent, + JsonSerializerOptions serializerOptions, + CancellationToken cancellationToken + ) + where TResponse : class + { + // We could use + // `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_ + return await Query( + httpMethod, + 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()); + return await JsonSerializer.DeserializeAsync( + responseStream, + serializerOptions, + cancellationToken + ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); + + }, + httpRequestContent, + cancellationToken + ); + } + + private async Task Query( + HttpMethod httpMethod, + Uri uri, + Func> read, + HttpContent? httpContent, + CancellationToken cancellationToken + ) + { + using var httpClient = CreateHttpClientAsync(); + using var httpRequestMessage = new HttpRequestMessage( + httpMethod, + uri + ); + httpRequestMessage.Content = httpContent; + using var httpResponseMessage = + await httpClient.SendAsync(httpRequestMessage, cancellationToken); + if (httpResponseMessage.StatusCode is not HttpStatusCode.OK) + { + throw new HttpRequestException( + $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode} and the response is: {httpResponseMessage.Content.ReadAsStringAsync(cancellationToken)}", + null, + httpResponseMessage.StatusCode + ); + } + 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 + ) + { + // 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 System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json); + return result; + } +} \ No newline at end of file diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs new file mode 100644 index 00000000..c25d53c1 --- /dev/null +++ b/backend/src/Services/CacheService.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Caching.Memory; +using static Database.ApiRequests.QueryCurrentUserOrApplication; + +namespace Database.Services; + +public sealed class CacheService( + IMemoryCache currentUserOrApplicationCache +) +{ + public CurrentUserOrApplication? SetCurrentUserOrApplication(string token, CurrentUserOrApplication userOrApplication) + { + var cacheEntryOptions = + new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromHours(1)); + return currentUserOrApplicationCache.Set(token, userOrApplication, cacheEntryOptions); + } + + public bool TryGetCurrentUserOrApplication(string token, [NotNullWhen(true)] out CurrentUserOrApplication? cachedUserOrApplication) + { + return currentUserOrApplicationCache.TryGetValue(token, out cachedUserOrApplication); + } +} \ No newline at end of file diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs deleted file mode 100644 index e6e19d04..00000000 --- a/backend/src/Services/EmailSender.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Threading.Tasks; -using MailKit.Net.Smtp; -using MailKit.Security; -using Microsoft.Extensions.Logging; -using MimeKit; - -namespace Database.Services -{ - public sealed class EmailSender - : IEmailSender - { - private readonly string _smtpHost; - private readonly int _smtpPort; - private readonly ILogger _logger; - - public EmailSender( - string smtpHost, - int smtpPort, - ILogger logger - ) - { - _smtpHost = smtpHost; - _smtpPort = smtpPort; - _logger = logger; - } - - public Task SendAsync( - (string name, string address) to, - string subject, - string body - ) - { - _logger.LogDebug($"About to send email to `{to}` with subject `{subject}` and body `{body}`"); - var message = new MimeMessage(); - message.From.Add( - new MailboxAddress( - "Database", - "database@solarbuildingenvelopes.com" - ) - ); - 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); - } - } -} \ No newline at end of file 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/Services/IEmailSender.cs b/backend/src/Services/IEmailSender.cs deleted file mode 100644 index 10845501..00000000 --- a/backend/src/Services/IEmailSender.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Threading.Tasks; - -namespace Database.Services -{ - public interface IEmailSender - { - public Task SendAsync( - (string name, string address) to, - string subject, - string body - ); - } -} \ 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..343f42c6 --- /dev/null +++ b/backend/src/Services/MethodFactory.cs @@ -0,0 +1,19 @@ +using System; +using Database.Methods; +using Database.Methods.SpectralToIntegral; +using Database.Methods.TransmittanceSum; + +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/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs new file mode 100644 index 00000000..16ab97a1 --- /dev/null +++ b/backend/src/Services/ResponseApprovalService.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using Database.Json; +using GraphQL; +using HotChocolate; +using HotChocolate.Execution; +using Microsoft.Extensions.Logging; +using NodaTime; + +namespace Database.Services; + +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Querying all meta data for {DataType} with ID {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 QueryAndVariablesAndResponse(this ILogger logger, string query, JsonElement variables, string response); +} + +public sealed class ResponseApprovalService( + AppSettings appSettings, + SigningService signingService, + IRequestExecutorProvider requestExecutorProvider, + IClock clock, + 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_lifeCycleDataQueryFileNames = + [ + "DataFields.graphql", + "LifeCycleDataFields.graphql", + "LifeCycleData.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. + /// + /// + /// + /// Thows exception, when singing of data failed. + /// + public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) + { + 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 > 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()) ?? [])})"))}"); + } + var (signature, fingerprint) = await signingService.SignDataAsync(response, cancellationToken); + return new ResponseApproval( + clock.GetUtcNow(), + signature, + fingerprint, + query, + variables, + response, + appSettings.OperatorId + ); + } + + private async Task<(string Query, JsonElement Variables, string Response)> Query(IData dataObject, CancellationToken cancellationToken) + { + logger.Query(dataObject.GetType(), dataObject.Id); + return dataObject switch + { + 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()}"), + }; + } + + 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 requestExecutorProvider.GetExecutorAsync(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 diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs new file mode 100644 index 00000000..26d816f5 --- /dev/null +++ b/backend/src/Services/SigningService.cs @@ -0,0 +1,193 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Database.Services; + +[SuppressMessage("Naming", "CA1707")] +public enum SigantureVerificationResult +{ + FAILED_RECEIVING_KEY, + GOOD_SIGNATURE, + BAD_SIGNATURE +} + +public static partial class Log +{ + [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 AssertGnuPgKeyExistenceAsync(CancellationToken cancellationToken) + { + var (success, output, diagnostics) = await ExecuteCommandAsync( + $"gpg --batch --with-colons --list-keys | grep '{appSettings.GnupgSecretSigningKey.Fingerprint}'", + null, + cancellationToken + ); + if (!success) + { + 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}'."); + } + } + + public async Task<(string Signature, string Fingerprint)> SignDataAsync( + string data, + CancellationToken cancellationToken + ) + { + var (success, signature, diagnostics) = await ExecuteCommandAsync( + $"gpg --pinentry-mode loopback --batch --passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' --detach-sig --armor --local-user '{appSettings.GnupgSecretSigningKey.Fingerprint}'", + data, + cancellationToken + ); + if (!success) + { + throw new InvalidOperationException($"Failed to create signature with standard output '{signature}' and diagnostic information '{diagnostics}'"); + } + logger.CreatedSignature(signature); + return (signature, appSettings.GnupgSecretSigningKey.Fingerprint); + } + + public async Task VerifySignatureAsync( + string message, + string signature, + string fingerprint, + CancellationToken cancellationToken + ) + { + if (!await ReceiveKeyAsync(fingerprint, cancellationToken)) + { + return SigantureVerificationResult.FAILED_RECEIVING_KEY; + } + var temporaryMessageFilePath = Path.GetTempFileName(); + try + { + File.WriteAllText(temporaryMessageFilePath, message); + var (success, output, diagnostics) = await ExecuteCommandAsync( + $"gpg --pinentry-mode loopback --batch --always-trust --assert-signer '{fingerprint}' --verify - '{temporaryMessageFilePath}'", + signature, + cancellationToken + ); + 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 ReceiveKeyAsync( + string fingerprint, + CancellationToken cancellationToken + ) + { + var (success, output, diagnostics) = await ExecuteCommandAsync( + $"gpg --pinentry-mode loopback --batch --keyserver {KeyServerUrl.AbsoluteUri} --receive-keys {fingerprint}", + null, + cancellationToken + ); + if (!success) + { + logger.FailedToReceiveKey(fingerprint, KeyServerUrl, output, diagnostics); + } + return success; + } + + private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommandAsync( + string command, + string? input, + CancellationToken cancellationToken + ) + { + logger.ExecuteCommand(command); + var process = Process.Start( + new ProcessStartInfo() + { + 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, + } + ) ?? 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); + } + process.StandardInput.Close(); + 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); + return (process.ExitCode is 0, output, diagnostics); + } +} \ 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..ecc6564d --- /dev/null +++ b/backend/src/Services/UserService.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Extensions; +using Microsoft.AspNetCore.Http; +using OpenIddict.Abstractions; + +namespace Database.Services; + +/// +/// Service to fetch current user or institution from the metabase or the local cache +/// +public sealed class UserService( + QueryCurrentUserOrApplication queryCurrentUserOrApplication, + IHttpContextAccessor httpContextAccessor, + CacheService cacheService +) +{ + /// + /// Get client ID from user claims. + /// + public string? GetOpenIdConnectApplicationClientId() + { + // `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 SwitchUserOrApplicationAsync( + Func> handleUser, + Func> handleApplication, + CancellationToken cancellationToken + ) + { + var userOrApplication = await FetchCurrentUserOrApplicationAsync(cancellationToken); + if (userOrApplication.CurrentApplication is not null) + { + return await handleApplication(userOrApplication.CurrentApplication, userOrApplication.currentOpenIdConnectTokenClientId); + } + else + { + return await handleUser(userOrApplication.CurrentUser, userOrApplication.currentOpenIdConnectTokenClientId); + } + } + + public async Task FetchCurrentUserOrApplicationAsync( + CancellationToken cancellationToken + ) + { + var httpContext = httpContextAccessor.HttpContext; + if (httpContext is null) + { + return QueryCurrentUserOrApplication.Empty; + } + var token = httpContext.ExtractBearerToken(); + if (token is null) + { + 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 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.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) + { + // If it is not cached, fetch it ... + cachedUserOrApplication = await queryCurrentUserOrApplication.Do( + cancellationToken + ); + // ... and store it in the cache. + cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); + } + return cachedUserOrApplication; + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 826343bd..571b5b39 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,224 +1,469 @@ 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; +using Database.ApiRequests; using Database.Configuration; +using Database.Data; using Database.Data.Extensions; -using HotChocolate.AspNetCore; +using Database.Enumerations; +using Database.GraphQl; +using Database.Services; +using Laraue.EfCoreTriggers.PostgreSql.Extensions; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; 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; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi; +using NodaTime; +using Npgsql; +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; +using Scalar.AspNetCore; using Serilog; -namespace Database -{ - public sealed class Startup - { - private readonly IWebHostEnvironment _environment; - private readonly AppSettings _appSettings; +namespace Database; - public Startup( - IWebHostEnvironment environment, - IConfiguration configuration - ) +public sealed class Startup( + IWebHostEnvironment environment, + IConfiguration configuration +) +{ + private readonly AppSettings _appSettings = + configuration.Get(_ => { - _environment = environment; - _appSettings = configuration.Get(); - } + _.BindNonPublicProperties = true; + _.ErrorOnUnknownConfiguration = false; + }) + ?? throw new InvalidOperationException("Failed to get application settings from configuration."); - public void ConfigureServices(IServiceCollection services) - { - AuthConfiguration.ConfigureServices(services, _appSettings); - GraphQlConfiguration.ConfigureServices(services, _environment); - ConfigureDatabaseServices(services); - ConfigureMessageSenderServices(services); - ConfigureRequestResponseServices(services); - ConfigureSessionServices(services); - services.AddHttpClient(); - services - .AddHealthChecks() - .AddDbContextCheck(); - services.AddSingleton(_appSettings); - services.AddSingleton(_environment); - // services.AddDatabaseDeveloperPageExceptionFilter(); - } + private readonly IClock _clock = SystemClock.Instance; - private static void ConfigureRequestResponseServices(IServiceCollection services) + public void ConfigureServices(IServiceCollection services) + { + ConfigureDatabaseServices(services); + ConfigureRequestResponseServices(services); + // ConfigureSessionServices(services); // Not used + ConfigureTelemetryServices(services); + services.AddAntiforgery(_ => { - // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forwarded-headers-middleware-order - services.Configure(_ => + _.HeaderName = AntiforgeryConstants.HeaderName; + }); + services + .AddDataProtection() + .PersistKeysToDbContext(); + ConfigureHttpClientServices(services); + services.AddHttpContextAccessor(); + services + .AddHealthChecks() + .AddApplicationLifecycleHealthCheck() + .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) + { + // 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; + _.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 - _.KnownNetworks.Clear(); + _.KnownIPNetworks.Clear(); _.KnownProxies.Clear(); } - ); - services.AddCors(_ => - _.AddDefaultPolicy(policy => + ); + services.AddCors(_ => + _.AddPolicy( + GraphQlConstants.CorsPolicy, + policy => policy - .AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod() - ) + .AllowAnyOrigin() + .AllowAnyHeader() + .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.AddOpenApi(OpenApiConstants.DocumentName, _ => + { + _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; + _.AddScalarTransformers(); + }); + } + + // private static void ConfigureSessionServices( + // IServiceCollection services + // ) + // { + // // 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 void ConfigureTelemetryServices( + IServiceCollection services + ) + { + services.AddOpenTelemetry() + // .WithTracing(_ => + // { + // _.AddAspNetCoreInstrumentation(); + // _.AddHttpClientInstrumentation(); + // _.AddHotChocolateInstrumentation(); + // _.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; + } ); - services.AddControllersWithViews(); - } + // if (!environment.IsProduction()) + // { + // _.AddConsoleExporter(); + // } + } + ); + } - private void ConfigureMessageSenderServices(IServiceCollection services) + private void ConfigureDatabaseContext( + DbContextOptionsBuilder options + ) + { + options + .UseNpgsql( + _appSettings.Database.ConnectionString(), + _ => _ + // Keep version in sync with the one in ./docker-compose.*.yaml + .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 + .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.LogicalCombinatorTypeName, _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) + .UseOpenIddict() + .UseProjectables() + .UsePostgreSqlTriggers(); + if (!environment.IsProduction()) + { + options + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + } + if (environment.IsEnvironment(Program.TestEnvironment)) { - services.AddTransient(serviceProvider => - new Services.EmailSender( - _appSettings.Email.SmtpHost, - _appSettings.Email.SmtpPort, - serviceProvider.GetRequiredService>() - ) + options.ConfigureWarnings(_ => + _.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) ); } + } + + 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`. + services.AddDbContext(options => + { }, + contextLifetime: ServiceLifetime.Transient, + optionsLifetime: ServiceLifetime.Singleton + ); + // services.ConfigureDbContext( + // ConfigureDatabaseContext, + // optionsLifetime: ServiceLifetime.Singleton + // ); + } - private static void ConfigureSessionServices(IServiceCollection services) + private void ConfigureHttpClientServices(IServiceCollection services) + { + services.AddHttpClient(); + var httpClientBuilder = services.AddHttpClient(ApiRequestService.CustomHttpClient); + if (environment.IsDevelopment()) { - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state - services.AddDistributedMemoryCache(); - services.AddSession(options => + httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler { - // 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; - }); + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } + ); + } + } + + public static void ConfigureCustomServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + 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/ + if (environment.IsDevelopment() || environment.IsEnvironment(Program.TestEnvironment)) + { + 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 the reverse proxy, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ } - private void ConfigureDatabaseServices(IServiceCollection services) + 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(); + app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) + app.UseRouting(); + // [Localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) + app.UseRequestLocalization(_ => + { + _.AddSupportedCultures("en-US"); + _.AddSupportedUICultures("en-US"); + _.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.UseWhen(context => context.Request.Path.StartsWithSegments("/connect"), appBuilder => + { + app.UseAuthentication(); + }); + app.UseAuthorization(); + app.UseAntiforgery(); + // app.UseSession(); // Not used + // app.UseResponseCompression(); // Done by the reverse proxy + // app.UseResponseCaching(); // Done by the reverse proxy + // app.UseWebSockets(); + app.MapOpenApi(OpenApiConstants.RoutePattern); + app.MapScalarApiReference(OpenApiConstants.DocsRoute, _ => { - services.AddPooledDbContextFactory(options => + _.Servers = []; // https://github.com/dotnet/aspnetcore/issues/57332#issuecomment-2480939916 + _.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() + .RequireCors(GraphQlConstants.CorsPolicy); + app.MapControllers(); + app.MapHealthChecks("/health", + new HealthCheckOptions + { + ResponseWriter = WriteJsonResponse + } + ) + .DisableHttpMetrics() + .AddOpenApiOperationTransformer((operation, context, cancellationToken) => + { + operation.Description = "Checks whether the webserver is healthy."; + var responseContent = new Dictionary { - options - .UseNpgsql(_appSettings.Database.ConnectionString) - .UseSchemaName(_appSettings.Database.SchemaName); - /* .UseNodaTime() */ // https://www.npgsql.org/efcore/mapping/nodatime.html - if (!_environment.IsProduction()) + [MediaTypeNames.Application.Json] = new OpenApiMediaType { - options - .EnableSensitiveDataLogging() - .EnableDetailedErrors(); + Schema = new OpenApiSchema { Type = JsonSchemaType.Object } } - } - ); - // Database context as services are used by `Identity`. - services.AddDbContext( - (services, options) => + }; + operation.Responses = new OpenApiResponses { - if (!_environment.IsProduction()) + ["200"] = new OpenApiResponse { - options - .EnableSensitiveDataLogging() - .EnableDetailedErrors(); + Description = "Healthy", + Content = responseContent + }, + ["503"] = new OpenApiResponse + { + Description = "Unhealthy", + Content = responseContent } - services - .GetRequiredService>() - .CreateDbContext(); - }, - ServiceLifetime.Transient - ); - } + }; + return Task.CompletedTask; + }); + } - 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 = MediaTypeNames.Application.Json; + var options = new JsonWriterOptions { Indented = true }; + using var memoryStream = new MemoryStream(); + using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) { - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ - if (_environment.IsDevelopment() || _environment.IsEnvironment("test")) + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("status", healthReport.Status.ToString()); + jsonWriter.WriteString("duration", healthReport.TotalDuration.ToString()); + jsonWriter.WriteStartObject("results"); + foreach (var healthReportEntry in healthReport.Entries) { - 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(); - 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.UseEndpoints(_ => - { - _.MapGraphQL().WithOptions( - // https://chillicream.com/docs/hotchocolate/server/middleware - new GraphQLServerOptions + 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) { - EnableSchemaRequests = true, - EnableGetRequests = false, - // AllowedGetOperations = AllowedGetOperations.Query - EnableMultipartRequests = false, - Tool = { - DisableTelemetry = true, - Enable = true, // _environment.IsDevelopment() - IncludeCookies = false, - GraphQLEndpoint = "/graphql", - HttpMethod = DefaultHttpMethod.Post, - HttpHeaders = new HeaderDictionary - { - { "Content-Type", "application/json" } - }, - Title = "GraphQL" - } + jsonWriter.WriteString("stackTrace", exception.StackTrace); } - ); - _.MapControllers(); - _.MapHealthChecks("/health", - new HealthCheckOptions + + if (exception.InnerException is not null) { - ResponseWriter = JsonResponseWriter + jsonWriter.WriteString("innerException", exception.InnerException.ToString()); } - ); - }); - } - private async Task JsonResponseWriter(HttpContext context, HealthReport report) - { - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync( - context.Response.Body, - report, - new JsonSerializerOptions + 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) { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase + jsonWriter.WritePropertyName(item.Key); + JsonSerializer.Serialize(jsonWriter, item.Value, + item.Value?.GetType() ?? typeof(object)); } - ).ConfigureAwait(false); + + 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 deleted file mode 100644 index 9bae5ba3..00000000 --- a/backend/src/Utilities/FileHelpers.cs +++ /dev/null @@ -1,269 +0,0 @@ -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; -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 - { - // 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 { new byte[] { 0x47, 0x49, 0x46, 0x38 } } }, - { ".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 }, - new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 }, - new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 }, - new byte[] { 0x50, 0x4B, 0x05, 0x06 }, - new byte[] { 0x50, 0x4B, 0x07, 0x08 }, - new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 }, - } - }, - }; - - // **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(".", - StringComparison.Ordinal) + 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."); - - 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( - 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(); - } - - public static async Task ProcessStreamedFile( - MultipartSection section, ContentDispositionHeaderValue contentDisposition, - ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) - { - 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(); - } - } - catch (Exception ex) - { - modelState.AddModelError("File", - "The upload failed. Please contact the Help Desk " + - $" for support. Error: {ex.HResult}"); - // Log the exception - } - - return Array.Empty(); - } - - 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.ContainsKey(ext)) - { - 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 = _fileSignature[ext]; - 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 f6852c3f..11d6f8cc 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -1,53 +1,21 @@ using System; -using System.IO; +using System.Diagnostics.Contracts; using Microsoft.Net.Http.Headers; -namespace Database.Utilities -{ - 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."); - } +namespace Database.Utilities; - 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 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. + [Pure] + public static string? GetBoundary(string? contentType) => + HeaderUtilities.RemoveQuotes( + MediaTypeHeaderValue.Parse(contentType).Boundary + ).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)); - } - } + [Pure] + public static bool IsMultipartContentType(string? contentType) => + !string.IsNullOrEmpty(contentType) + && contentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase); } \ 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..d2d013e3 --- /dev/null +++ b/backend/src/Utilities/Sha256FileHasher.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.Contracts; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Database.Utilities; + +public static class Sha256FileHasher +{ + [Pure] + public static async Task ComputeForFile(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) + ); + } + + [Pure] + public static string ComputeForString(string content) + { + return Convert.ToHexString( + SHA256.HashData( + Encoding.UTF8.GetBytes(content) + ) + ); + } +} \ No newline at end of file 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..bd86c2b5 --- /dev/null +++ b/backend/src/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using Database.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"; +} diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index d41949da..e5bcf976 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -4,12 +4,10 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Debug", - "System": "Debug", - "Microsoft": "Debug" + "Default": "Verbose", + "Override": { + "Microsoft.AspNetCore.Diagnostics.HealthChecks": "Warning" + } } - }, - "Database": { - "ConnectionString": "Host=database; Port=5432; Database=xbase_development; User Id=postgres; Password=postgres; Maximum Pool Size=90;" } -} +} \ No newline at end of file diff --git a/backend/src/appsettings.json b/backend/src/appsettings.json index 50baee85..6b859078 100644 --- a/backend/src/appsettings.json +++ b/backend/src/appsettings.json @@ -1,6 +1,3 @@ { - "AllowedHosts": "*", - "Database": { - "SchemaName": "database" - } -} + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/backend/src/appsettings.production.json b/backend/src/appsettings.production.json index e331280a..ab4459f9 100644 --- a/backend/src/appsettings.production.json +++ b/backend/src/appsettings.production.json @@ -5,16 +5,10 @@ "Serilog": { "MinimumLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information", "Override": { - "Microsoft": "Error", - "Microsoft.AspNetCore": "Warning", - "System": "Error" + "System": "Warning", + "Microsoft": "Warning" } } - }, - "Database": { - "ConnectionString": "Host=database; Port=5432; Database=xbase; User Id=postgres; Passfile=/run/secrets/postgres_passwords; Maximum Pool Size=90;" } } diff --git a/backend/src/appsettings.test.json b/backend/src/appsettings.test.json index 19b6f55c..424acc40 100644 --- a/backend/src/appsettings.test.json +++ b/backend/src/appsettings.test.json @@ -4,12 +4,7 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Error", - "Override": { - "Microsoft": "Error", - "Microsoft.AspNetCore": "Warning", - "System": "Error" - } + "Default": "Error" } } } diff --git a/backend/test/AuditableTests.cs b/backend/test/AuditableTests.cs new file mode 100644 index 00000000..f7f5ea36 --- /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); + await 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); + // await 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 f8e9b348..e2e52aa6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,38 +1,36 @@ - + + + - net5.0 - 9.0 - false - enable - true - true - false + true + true + Exe - - - - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - PreserveNewest - + + + + @@ -44,4 +42,5 @@ + diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs new file mode 100644 index 00000000..dd53fa37 --- /dev/null +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -0,0 +1,145 @@ +// 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.Threading.Tasks; +using Serilog; +using Database.Data; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Globalization; + +namespace Database.Tests.Integration; + +public sealed class CustomWebApplicationFactory + : WebApplicationFactory +{ + private bool _disposed; + + public CustomWebApplicationFactory() + { + } + + 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 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.ConfigureServices(serviceCollection => + { + using var scope = serviceCollection.BuildServiceProvider().CreateScope(); + // Configure `AppSettings` + var appSettings = scope.ServiceProvider.GetRequiredService(); + 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. + // 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 + // { + // Name = databaseName + // // SchemaName = schemaName + // } + // } + // ; + // var appSettingsServiceDescriptor = + // serviceCollection.SingleOrDefault(d => + // d.ServiceType == typeof(AppSettings) + // ); + // if (appSettingsServiceDescriptor is not null) + // { + // serviceCollection.Remove(appSettingsServiceDescriptor); + // } + // serviceCollection.AddSingleton(newAppSettings); + } + ); + } + + 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() + { + 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/DatabaseSchemaTests.cs b/backend/test/Integration/DatabaseSchemaTests.cs new file mode 100644 index 00000000..08a59329 --- /dev/null +++ b/backend/test/Integration/DatabaseSchemaTests.cs @@ -0,0 +1,149 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Npgsql; +using NUnit.Framework; + +namespace Database.Tests.Integration; + +[TestFixture] +public sealed class DatabaseSchemaTests + : IntegrationTests +{ + /// + /// 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() + { + // 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." + ); + } + + private static Task ReadSqlScriptAsync( + string scriptPath, + CancellationToken cancellationToken + ) + { + 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); + } + + private static async Task ExecuteSqlScriptAsync( + string connectionString, + string script, + CancellationToken cancellationToken + ) + { + 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; + } + } + + private static async Task<(int ExitCode, string Output, string Diagnostics)> ExecuteCommandAsync( + string command, + string? input, + CancellationToken cancellationToken + ) + { + var process = Process.Start( + new ProcessStartInfo() + { + 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, + } + ) ?? 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); + } + 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/GraphQl/GraphQlSchemaTests.cs b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs new file mode 100644 index 00000000..d9bc142f --- /dev/null +++ b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs @@ -0,0 +1,31 @@ +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 + 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/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap new file mode 100644 index 00000000..751e5e99 --- /dev/null +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -0,0 +1,4185 @@ +schema { + query: Query + mutation: Mutation +} + +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") + 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") + 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") + 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") + 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." + after: String + "Returns the elements in the list that come before the 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") + 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! +} + +type Mutation { + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! + @cost(weight: "10") + clearInstitutionAccessPolicies(input: ClearInstitutionAccessPoliciesInput!): ClearInstitutionAccessPoliciesPayload! + @cost(weight: "10") + clearOpenIdConnectApplicationAccessPolicies( + input: ClearOpenIdConnectApplicationAccessPoliciesInput! + ): 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!): ConfigureDataAccessPolicyPayload! + @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") + 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") + setInstitutionAccessPolicy(input: SetInstitutionAccessPolicyInput!): SetInstitutionAccessPolicyPayload! + @cost(weight: "10") + setOpenIdConnectApplicationAccessPolicy( + input: SetOpenIdConnectApplicationAccessPolicyInput! + ): SetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + setUserAccessPolicy(input: SetUserAccessPolicyInput!): SetUserAccessPolicyPayload! + @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") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! + @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 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 { + accessPolicy: DataAccessPolicy + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + gValues: [Float!]! + id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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 ClearInstitutionAccessPoliciesError implements UserError { + code: ClearInstitutionAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearInstitutionAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ClearInstitutionAccessPoliciesError!] + query: Query! +} + +type ClearOpenIdConnectApplicationAccessPoliciesError implements UserError { + code: ClearOpenIdConnectApplicationAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearOpenIdConnectApplicationAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ClearOpenIdConnectApplicationAccessPoliciesError!] + query: Query! +} + +type ClearUserAccessPoliciesError implements UserError { + code: ClearUserAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearUserAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ClearUserAccessPoliciesError!] + query: Query! +} + +type Component { + name: String! + 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 +} + +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 CurrentOpenIdConnectApplication { + clientId: String! + displayName: String + owner: CurrentInstitution! +} + +type CurrentUser { + databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnection! + isAtLeastAssistantManagerOfDatabaseOperator: Boolean! + name: String! + representedInstitutions: UserRepresentedInstitutionConnection! + 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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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 implements Node { + combinator: LogicalCombinator! + createdAt: DateTime! + 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 + ): InstitutionAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + isAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + isEveryoneAllowed: Boolean! + 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 + ): 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 + ): UserAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + uuid: Uuid! +} + +"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! + keyFingerprint: String! + message: String! + query: GraphQlQuery! + 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: Int! +} + +type DatabaseOperatingManagedInstitutionConnection { + totalCount: Int! +} + +type DatabaseOperatorEdge { + node: Institution! +} + +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 { + accessPolicy: DataAccessPolicy + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + heights: [Float!]! + id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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 { + absoluteFilePath: String! + 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 { + accessPolicy: DataAccessPolicy + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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 InstitutionAccessPolicy implements Node { + accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") + dataAccessPolicyId: Uuid! + id: ID! + institution: Institution @cost(weight: "3") + institutionId: Uuid! + 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: Int! +} + +type InstitutionManagedInstitutionEdge { + node: InstitutionManagedInstitutionNode! +} + +type InstitutionManagedInstitutionNode { + name: String! + uuid: Uuid! +} + +type LifeCycleData implements Node & Data { + accessPolicy: DataAccessPolicy + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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 OpenIdConnectApplication { + clientId: String! + displayName: String + uuid: Uuid! +} + +type OpenIdConnectApplicationAccessPolicy implements Node { + accessCountSinceStartTime: CountSinceTime + client: OpenIdConnectApplication @cost(weight: "3") + clientId: String! + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") + id: ID! + 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! + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + infraredEmittances: [Float!]! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + publishingState: PublishingState! + 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 { + accessPolicy: DataAccessPolicy + 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! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsEveryoneAllowed: Boolean! + IsNobodyAllowed: Boolean! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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: 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: Doi + "Referenced section" + section: String + title: String + "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 +} + +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! + 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 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! + message: String! + query: GraphQlQuery! + 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! +} + +type SetInstitutionAccessPolicyError implements UserError { + code: SetInstitutionAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type SetInstitutionAccessPolicyPayload { + errors: [SetInstitutionAccessPolicyError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type SetOpenIdConnectApplicationAccessPolicyError implements UserError { + code: SetOpenIdConnectApplicationAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type SetOpenIdConnectApplicationAccessPolicyPayload { + errors: [SetOpenIdConnectApplicationAccessPolicyError!] + openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + query: Query! +} + +type SetUserAccessPolicyError implements UserError { + code: SetUserAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type SetUserAccessPolicyPayload { + errors: [SetUserAccessPolicyError!] + 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 + 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!]! + method: Method @cost(weight: "3") + methodId: Uuid! + sourceName: String! +} + +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! + path: [String!]! +} + +type UpdateChildGetHttpsResourcePayload { + errors: [UpdateChildGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! + message: String! + path: [String!]! +} + +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] + query: Query! +} + +type UpdateDataError implements UserError { + code: UpdateDataErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] + 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 UpperLimitPerDuration { + duration: Duration + upperLimit: NonNegativeInt! +} + +type User { + name: String! + uuid: Uuid! +} + +type UserAccessPolicy implements Node { + accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") + dataAccessPolicyId: Uuid! + id: ID! + isAccessAllowed(userId: Uuid): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! + isWithinTimeSpan: Boolean! + updatedAt: DateTime! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration + user: User @cost(weight: "3") + userId: Uuid! + 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 + emailVerified: Boolean + name: String! + phoneNumber: String + phoneNumberVerified: Boolean + roles: [String!] + sub: String! + website: String +} + +type UserRepresentedInstitutionConnection { + edges: [UserRepresentedInstitutionEdge!]! + totalCount: Int! +} + +type UserRepresentedInstitutionEdge { + node: UserRepresentedInstitutionNode! + role: InstitutionRepresentativeRole! +} + +type UserRepresentedInstitutionNode { + managedInstitutions: InstitutionManagedInstitutionConnection! + name: String! + uuid: Uuid! +} + +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: GraphQlQuery! + signature: String! + timestamp: DateTime! + variables: Any! +} + +interface Data { + accessPolicy: DataAccessPolicy + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + component: Component + componentId: Uuid! + createdAt: DateTime! + creator: Institution + creatorId: Uuid! + database: Database + databaseId: Uuid! + description: String + id: ID! + kind: DataKind! + locale: Locale! + name: String + publishingState: PublishingState! + 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 { + approverId: Uuid! + dataId: Uuid! + dataKind: DataKind! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: ReferenceInput! + timestamp: DateTime! + variables: Any! +} + +input AppliedMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sources: [NamedMethodSourceInput!]! +} + +input AppliedMethodPropositionInput { + and: [AppliedMethodPropositionInput!] + arguments: NamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [AppliedMethodPropositionInput!] + sources: NamedMethodSourcesPropositionInput +} + +input AppliedMethodSortInput { + methodId: SortEnumType @cost(weight: "10") +} + +input BooleanPropositionInput { + equalTo: Boolean @cost(weight: "10") + notEqualTo: Boolean @cost(weight: "10") +} + +input BytePropositionInput { + equalTo: Byte @cost(weight: "10") + greaterThan: Byte @cost(weight: "10") + greaterThanOrEqualTo: Byte @cost(weight: "10") + in: [Byte] @cost(weight: "10") + lessThan: 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 CalorimetricDataPropositionInput { + and: [CalorimetricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + gValues: FloatsPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [CalorimetricDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + 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") + 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! + illuminant: Illuminant + lStar: Float! + observer: CalorimetricObserver +} + +input CielabColorPropositionInput { + and: [CielabColorPropositionInput!] + aStar: FloatPropositionInput + bStar: FloatPropositionInput + illuminant: IlluminantPropositionInput + lStar: FloatPropositionInput + 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 +} + +input ClearOpenIdConnectApplicationAccessPoliciesInput { + data: DataReferenceInput +} + +input ClearUserAccessPoliciesInput { + data: DataReferenceInput +} + +input ClosedIntervalInput { + lowerBound: Float! + 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! +} + +""" +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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput + isEveryoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput + opticalData: OpticalDataPropositionInput + or: [ConfigureDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput +} + +input ConfigureDataAccessPolicyInput { + combinator: LogicalCombinator! + data: DataReferenceInput +} + +input CountSinceTimePropositionInput { + accessCount: NonNegativeIntPropositionInput + and: [CountSinceTimePropositionInput!] + or: [CountSinceTimePropositionInput!] + startTime: DateTimePropositionInput +} + +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 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!]! + 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 CreateResponseApprovalsPropositionInput { + and: [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 +} + +input CrossDatabaseDataReferenceInput { + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +input CrossDatabaseDataReferencePropositionInput { + and: [CrossDatabaseDataReferencePropositionInput!] + databaseId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput + dataTimestamp: DateTimePropositionInput + or: [CrossDatabaseDataReferencePropositionInput!] +} + +""" +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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput + isEveryoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput + opticalData: OpticalDataPropositionInput + or: [DataAccessPolicyPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput +} + +""" +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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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 { + 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 { + and: [DataApprovalPropositionInput!] + approverId: UuidPropositionInput + keyFingerprint: StringPropositionInput + message: StringPropositionInput + or: [DataApprovalPropositionInput!] + query: StringPropositionInput + signature: StringPropositionInput + statement: ReferencePropositionInput + timestamp: DateTimePropositionInput + 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") + notEqualTo: DataKind @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") +} + +input DataPropositionInput { + and: [DataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [DataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput + warnings: StringsPropositionInput +} + +input DataReferenceInput { + dataId: Uuid! + dataKind: DataKind! +} + +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") + updatedAt: SortEnumType @cost(weight: "10") +} + +input DateTimePropositionInput { + equalTo: DateTime @cost(weight: "10") + greaterThan: DateTime @cost(weight: "10") + greaterThanOrEqualTo: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") + lessThan: 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") + greaterThan: Decimal @cost(weight: "10") + greaterThanOrEqualTo: Decimal @cost(weight: "10") + in: [Decimal] @cost(weight: "10") + lessThan: 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") +} + +input DeleteDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteGetHttpsResourceInput { + getHttpsResourceId: Uuid! +} + +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 { + dataFormatId: Uuid! + path: [String!]! +} + +input FileMetaInformationPropositionInput { + and: [FileMetaInformationPropositionInput!] + dataFormatId: UuidPropositionInput + or: [FileMetaInformationPropositionInput!] + path: StringsPropositionInput +} + +input FilesMetaInformationPropositionInput { + all: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") +} + +input FloatPropositionInput { + equalTo: Float @cost(weight: "10") + greaterThan: Float @cost(weight: "10") + greaterThanOrEqualTo: Float @cost(weight: "10") + in: [Float] @cost(weight: "10") + inClosedInterval: ClosedIntervalInput @cost(weight: "10") + lessThan: 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") +} + +input FloatsPropositionInput { + all: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: FloatPropositionInput @cost(weight: "10") + some: FloatPropositionInput @cost(weight: "10") +} + +input GeometricDataPropositionInput { + and: [GeometricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [GeometricDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + 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") + updatedAt: SortEnumType @cost(weight: "10") +} + +input GetHttpsResourcePropositionInput { + and: [GetHttpsResourcePropositionInput!] + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput + calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput + dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput + description: StringPropositionInput + geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + opticalData: OpticalDataPropositionInput + or: [GetHttpsResourcePropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput +} + +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") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + 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: FilesMetaInformationPropositionInput + createdAt: DateTimePropositionInput + dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + id: UuidPropositionInput + or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput + updatedAt: DateTimePropositionInput +} + +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") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") +} + +input HygrothermalDataPropositionInput { + and: [HygrothermalDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [HygrothermalDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + 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") + 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!] + 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 { + equalTo: Int @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + lessThan: 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 JsonElementPropositionInput { + and: [JsonElementPropositionInput!] + or: [JsonElementPropositionInput!] + valueKind: JsonValueKindPropositionInput +} + +input JsonValueKindPropositionInput { + equalTo: JsonValueKind @cost(weight: "10") + in: [JsonValueKind!] @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") + notIn: [JsonValueKind!] @cost(weight: "10") +} + +input LifeCycleDataPropositionInput { + and: [LifeCycleDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [LifeCycleDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + 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") + updatedAt: SortEnumType @cost(weight: "10") +} + +input LocalDatePropositionInput { + 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 { + 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 { + 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 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") + greaterThanOrEqualTo: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") + lessThan: 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") +} + +input NamedMethodArgumentInput { + name: String! + value: Any! +} + +input NamedMethodArgumentPropositionInput { + and: [NamedMethodArgumentPropositionInput!] + name: StringPropositionInput + or: [NamedMethodArgumentPropositionInput!] +} + +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! +} + +input NamedMethodSourcePropositionInput { + and: [NamedMethodSourcePropositionInput!] + name: StringPropositionInput + or: [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") + 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 NullableOfDataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + in: [DataKind] @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + notIn: [DataKind] @cost(weight: "10") +} + +input NumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + +input NumerationPropositionInput { + and: [NumerationPropositionInput!] + mainNumber: StringPropositionInput + or: [NumerationPropositionInput!] + prefix: StringPropositionInput + suffix: StringPropositionInput +} + +input NumerationSortInput { + mainNumber: SortEnumType @cost(weight: "10") + prefix: SortEnumType @cost(weight: "10") + 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!] + 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 { + 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: DataApprovalsPropositionInput + cielabColors: CielabColorsPropositionInput + coatedSide: CoatedSidePropositionInput + 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 + or: [OpticalDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + subtype: OpticalComponentSubtypePropositionInput + type: OpticalComponentTypePropositionInput + 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") + updatedAt: SortEnumType @cost(weight: "10") +} + +input PhotovoltaicDataPropositionInput { + and: [PhotovoltaicDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: DataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [PhotovoltaicDataPropositionInput!] + resources: GetHttpsResourcesPropositionInput + 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") + updatedAt: SortEnumType @cost(weight: "10") +} + +input PublicationInput { + abstract: String + arXiv: ArXiv + authors: [String!] + doi: Doi + section: String + title: String + urn: Url + webAddress: Url +} + +input PublicationPropositionInput { + abstract: StringPropositionInput + and: [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 + or: [PublicationPropositionInput!] + "Referenced section" + section: StringPropositionInput + title: StringPropositionInput + "A Uniform Resource Name (URN) can be used to define a publication." + urn: StringPropositionInput + "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 +} + +input PublicationSortInput { + 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." + urn: SortEnumType @cost(weight: "10") + "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") +} + +input PublishDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input RecomputeGetHttpsResourceHashValuesPropositionInput { + and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput + calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput + dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput + description: StringPropositionInput + geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + opticalData: OpticalDataPropositionInput + or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput +} + +input ReferenceInput @oneOf { + publication: PublicationInput + standard: StandardInput +} + +input ReferencePropositionInput { + and: [ReferencePropositionInput!] + or: [ReferencePropositionInput!] + publication: PublicationPropositionInput + standard: StandardPropositionInput +} + +input ReferenceSortInput { + publication: PublicationSortInput @cost(weight: "10") + standard: StandardSortInput @cost(weight: "10") +} + +input RemoveDataApprovalInput { + dataId: Uuid! + dataKind: DataKind! + 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). 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 '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 '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 +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +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. + +A data access policy is either the one-and-only global one or associated with a +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. + +There are mutations to +* 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; +* 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 set). + +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!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput + isEveryoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput + opticalData: OpticalDataPropositionInput + or: [ResetDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput +} + +input ResetDataAccessPolicyInput { + data: DataReferenceInput +} + +input RetractDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input RootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! +} + +input SetGetHttpsResourceParentInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + getHttpsResourceId: Uuid! + parentId: Uuid! +} + +input SetInstitutionAccessPolicyInput { + data: DataReferenceInput + institutionId: Uuid! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetOpenIdConnectApplicationAccessPolicyInput { + clientId: String! + data: DataReferenceInput + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetUserAccessPolicyInput { + data: DataReferenceInput + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput + userId: Uuid! +} + +input ShortPropositionInput { + equalTo: Short @cost(weight: "10") + greaterThan: Short @cost(weight: "10") + greaterThanOrEqualTo: Short @cost(weight: "10") + in: [Short] @cost(weight: "10") + lessThan: 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") +} + +input StandardInput { + abstract: String + locator: Url + numeration: NumerationInput! + section: String + standardizers: [Standardizer!]! + title: String + 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!] + locator: UrlPropositionInput + 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 { + 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") + 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!] + 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") +} + +input StringsPropositionInput { + all: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: StringPropositionInput @cost(weight: "10") + some: StringPropositionInput @cost(weight: "10") +} + +input ToTreeVertexAppliedConversionMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sourceName: String! +} + +input ToTreeVertexAppliedConversionMethodPropositionInput { + and: [ToTreeVertexAppliedConversionMethodPropositionInput!] + arguments: NamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [ToTreeVertexAppliedConversionMethodPropositionInput!] + sourceName: StringPropositionInput +} + +input ToTreeVertexAppliedConversionMethodSortInput { + methodId: SortEnumType @cost(weight: "10") + sourceName: SortEnumType @cost(weight: "10") +} + +input UnsetInstitutionAccessPolicyInput { + data: DataReferenceInput + institutionId: Uuid! +} + +input UnsetOpenIdConnectApplicationAccessPolicyInput { + clientId: String! + data: DataReferenceInput +} + +input UnsetUserAccessPolicyInput { + data: DataReferenceInput + userId: Uuid! +} + +input UpdateChildGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + +input UpdateDataInput { + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String + locale: String! + name: String + warnings: [String!]! +} + +input UpdateResponseApprovalsPropositionInput { + and: [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 +} + +input UpdateRootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + +input UpperLimitPerDurationInput { + duration: Duration + upperLimit: NonNegativeInt! +} + +input UpperLimitPerDurationPropositionInput { + and: [UpperLimitPerDurationPropositionInput!] + duration: DurationPropositionInput + or: [UpperLimitPerDurationPropositionInput!] + upperLimit: NonNegativeIntPropositionInput +} + +input UriSortInput { + absolutePath: SortEnumType @cost(weight: "10") + absoluteUri: 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") + isUnc: 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") + scheme: SortEnumType @cost(weight: "10") + userEscaped: SortEnumType @cost(weight: "10") + userInfo: SortEnumType @cost(weight: "10") +} + +input UrlPropositionInput { + equalTo: Url @cost(weight: "10") + greaterThan: Url @cost(weight: "10") + greaterThanOrEqualTo: Url @cost(weight: "10") + in: [Url] @cost(weight: "10") + lessThan: 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 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!] + 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") +} + +input UuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + lessThan: 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") +} + +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 + UNKNOWN_DATA + UNSUPPORTED_DATA_FORMAT + HASH_VALUE_CALCULATION_FAILED + HASH_VALUE_MISMATHCH +} + +enum CalorimetricObserver { + TWO_DEGREES + TEN_DEGREES +} + +enum ClearInstitutionAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum ClearUserAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum CoatedSide { + PRIME + NON_PRIME + BOTH + NEITHER + UNKNOWN + NOT_APPLICABLE +} + +enum ConfigureDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ConfigureDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +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 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 + 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 DatabaseVerificationState { + PENDING + VERIFIED +} + +enum DataKind { + CALORIMETRIC_DATA + GEOMETRIC_DATA + HYGROTHERMAL_DATA + LIFE_CYCLE_DATA + OPTICAL_DATA + PHOTOVOLTAIC_DATA +} + +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 InstitutionRepresentativeRole { + OWNER + ASSISTANT +} + +enum JsonValueKind { + UNDEFINED + OBJECT + ARRAY + STRING + NUMBER + TRUE + FALSE + NULL +} + +enum LogicalCombinator { + ALL + SOME +} + +enum OpticalComponentSubtype { + 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 +} + +enum OpticalComponentType { + GLAZING + SHADING +} + +enum PublishDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED + MISSING_ROOT_RESOURCE + DUPLICATE_ROOT_RESOURCE + RESOURCES_WITHOUT_FILE +} + +enum PublishingState { + PENDING + PUBLISHED + RETRACTED +} + +enum RecomputeGetHttpsResourceHashValuesErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + FAILED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum RemoveDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPROVAL + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum ResetDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ResetDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +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 SetInstitutionAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION +} + +enum SetOpenIdConnectApplicationAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION +} + +enum SetUserAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER +} + +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 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 + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE +} + +enum UpdateDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + +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 +} + +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") + +"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 with high precision." +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") + +"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](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." +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 an unsigned 32-bit numeric non-fractional value." +scalar NonNegativeInt + +"The `Short` scalar type represents a signed 16-bit integer." +scalar Short + @specifiedBy(url: "https://scalars.graphql.org/chillicream/short.html") + +"The `Upload` scalar type represents a file upload." +scalar Upload + +"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/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs new file mode 100644 index 00000000..6a12c0c9 --- /dev/null +++ b/backend/test/Integration/IntegrationTests.cs @@ -0,0 +1,434 @@ +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.Net.Mime; +using System.Runtime.CompilerServices; +using System.Text; +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; + +namespace Database.Tests.Integration; + +[FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +public abstract partial class IntegrationTests + : IDisposable +{ + private bool _disposed; + + protected IntegrationTests() + { + Factory = new CustomWebApplicationFactory(); + HttpClient = CreateHttpClient(); + } + + private CustomWebApplicationFactory Factory { get; } + + 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) + ); + } + + private static ByteArrayContent MakeJsonHttpContent( + TContent content + ) + { + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + JsonSerializerSettings.GraphQl + ) + ); + result.Headers.ContentType = + new MediaTypeHeaderValue(MediaTypeNames.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..d9c7e932 --- /dev/null +++ b/backend/test/Integration/MigrationTests.cs @@ -0,0 +1,60 @@ +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 +{ + /// + /// 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() + { + 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(), + 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 diff --git a/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json b/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json new file mode 100644 index 00000000..e3937263 --- /dev/null +++ b/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.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 new file mode 100644 index 00000000..e122fbb2 --- /dev/null +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -0,0 +1,111 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Text.Json; +using System.Threading.Tasks; +using Database.Json; +using Database.Methods.SpectralToIntegral; +using FluentAssertions; +using FluentAssertions.Execution; +using NUnit.Framework; + +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() + { + // Arrange + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([]) + ]); + var method = new SpectralToIntegralMethod(); + + // Act + Action act = () => method.Calculate(spectralDataPoints); + + // Assert + act.Should() + .Throw() + .WithMessage("The list `spectralDataPoints` is empty."); + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_One() + { + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), + ]) + ]); + var method = new SpectralToIntegralMethod(); + + // Act + Action act = () => method.Calculate(spectralDataPoints); + + // Assert + act.Should() + .Throw() + .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] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_Few() + { + 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); + 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.large.json") + ); + using var jsonDocument = await JsonDocument.ParseAsync( + fileStream, + JsonDocumentSettings.Lax + ); + var spectralDataPoints = jsonDocument.RootElement.Deserialize( + 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()) + { + 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 diff --git a/backend/test/Play.cs b/backend/test/Play.cs index ab7914b0..bcbcda76 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -1,15 +1,15 @@ 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!"); + await Task.FromResult(0); } } \ No newline at end of file diff --git a/backend/test/Program.cs b/backend/test/Program.cs deleted file mode 100644 index 3fc4eadf..00000000 --- a/backend/test/Program.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Database.Tests -{ - public class Program - { - public static int Main() - { - return Xunit.ConsoleClient.Program.Main( - new[] { - typeof(Program).Assembly.Location - } - ); - } - } -} 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 -} diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..e69de29b diff --git a/database.mk b/database.mk new file mode 100755 index 00000000..0b80f6fa --- /dev/null +++ b/database.mk @@ -0,0 +1,208 @@ +#!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +COMPOSE_BAKE=true + +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 + +psql : ## Enter PostgreSQL interactive terminal in the `database` container + docker compose up \ + --no-build \ + --no-recreate \ + --wait \ + database + docker compose exec \ + database \ + psql \ + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" +.PHONY : psql + +remove-volume : ## Remove data and files volumes + docker volume rm \ + "${NAME}_${ENVIRONMENT}_data" + docker volume rm \ + "${NAME}_${ENVIRONMENT}_files" +.PHONY : remove-volume + +create : ## Create database with name `${POSTGRES_DATABASE_NAME}` + docker compose up \ + --no-build \ + --no-recreate \ + --wait \ + database + docker compose exec \ + --no-tty \ + database \ + createdb \ + --username="${POSTGRES_USER}" \ + "${POSTGRES_DATABASE_NAME}" +.PHONY : create + +drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` + docker compose up \ + --no-build \ + --no-recreate \ + --wait \ + database + docker compose exec \ + --no-tty \ + database \ + dropdb \ + --username="${POSTGRES_USER}" \ + "${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 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-recreate \ + --wait \ + database + cat "${SCRIPT}" \ + | docker compose exec \ + --no-tty \ + database \ + psql \ + --echo-all \ + --no-psqlrc \ + --set=ON_ERROR_STOP=on \ + --file=- \ + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" +.PHONY : sql + +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 \ + --no-deps \ + 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 : DIR = ./backup +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 \ + --wait \ + database + docker compose exec \ + --no-tty \ + database \ + pg_dump \ + --clean \ + --if-exists \ + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" \ + | gzip \ + > "${DIR}/${dump_archive_name}" + docker compose run \ + --rm \ + --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 ' \ + 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 : 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 \ + --no-build \ + --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}" \ + "${POSTGRES_DATABASE_NAME}" + gunzip --stdout "${DIR}/${dump_archive_name}" \ + | docker compose exec \ + --no-tty \ + database \ + psql \ + --echo-all \ + --no-psqlrc \ + --set=ON_ERROR_STOP=on \ + --file=- \ + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" + docker compose run \ + --rm \ + --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 ' \ + if [[ "${ENVIRONMENT}" == "development" ]]; then \ + cd /app/src/files ; \ + else \ + cd /app/files ; \ + fi ; \ + rm \ + --recursive \ + --force \ + --dir \ + * ; \ + 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 diff --git a/deploy.mk b/deploy.mk new file mode 100755 index 00000000..11657193 --- /dev/null +++ b/deploy.mk @@ -0,0 +1,131 @@ +#!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +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:4.0.0 + +# 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 + +do : 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 + +restore : 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 + +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 \ + ./nginx/html/maintenance.html +.PHONY : begin-maintenance + +end-maintenance : ## End maintenance + rm --force \ + ./nginx/html/maintenance.html +.PHONY : end-maintenance + +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 + +fetch-all : ## Fetch all + git fetch --all +.PHONY : fetch-all + +# Inspired by https://grimoire.ca/git/stop-using-git-pull-to-deploy/ +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" +.PHONY : dotenv + +# 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 \ + --force-recreate \ + --renew-anon-volumes \ + --remove-orphans \ + --wait +.PHONY : services + +run-tests : ## TODO Run tests +.PHONY : run-tests + +# $(MAKE) --file=./docker.mk \ +# restart \ +# SERVICE="${SERVICE}" +restart : ## Restart service `${SERVICE}` and await its health + docker compose restart \ + --no-deps \ + ${SERVICE} +.PHONY : restart + +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 "./Makefile does not link to ./docker.mk" >&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}" >&2 ; \ + exit 2 ; \ + fi +.PHONY : symlink 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 diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml new file mode 100644 index 00000000..b4b3ac5f --- /dev/null +++ b/docker-compose.development.yaml @@ -0,0 +1,252 @@ +# Keep setup in sync with the one in ./docker-compose.production.yaml +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" + pull_policy: "missing" + + database: + # 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 + - ./.psqlrc:/etc/postgresql-common/psqlrc:ro + environment: + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" + POSTGRES_DB: "postgres" # default database used to run `./database.mk drop` + # 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 + # 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:?required}'"] + interval: "30s" + timeout: "5s" + retries: 3 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "missing" + + backend: + build: + context: "./backend" + dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" + depends_on: + database: + condition: "service_started" + volumes: + - ./backend:/app + - files:/app/src/files + - backend_vscode_server:/home/me/.vscode-server + - 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 + # 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 + # Create anonymous volumes for dotnet tools + - /home/me/.nuget + - /home/me/.dotnet + # ports: + # - "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" + ASPNETCORE_ENVIRONMENT: "development" + 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__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_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__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 + # 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" + # 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" + - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" + - "${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.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 + test: + [ + "CMD-SHELL", + "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1", + ] + interval: "30s" + timeout: "5s" + retries: 6 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + deploy: + resources: + limits: + cpus: "0.8" + pull_policy: "never" + stdin_open: true + + frontend: + build: + context: "./frontend" + dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" + depends_on: + backend: + condition: "service_started" + volumes: + - ./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 + environment: + NODE_ENV: "development" + HOST: "${HOST:?required}" + extra_hosts: + - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" + # user: "1000:1000" + # 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: "30s" + timeout: "5s" + retries: 6 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "never" + + reverse_proxy: + image: "nginx:1.31-trixie-otel" + depends_on: + backend: + condition: "service_started" + frontend: + condition: "service_started" + volumes: + - ./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 # ... + ports: + - "0.0.0.0:${HTTP_PORT:?required}:80" + command: + - "nginx-debug" + - "-g" + - "daemon off;" + restart: "unless-stopped" + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + interval: "30s" + timeout: "5s" + retries: 3 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "missing" + +volumes: + data: + files: + certificates: + gnupg: + backend_vscode_server: + backend_vscode_server_insiders: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml new file mode 100644 index 00000000..8eaac633 --- /dev/null +++ b/docker-compose.production.yaml @@ -0,0 +1,195 @@ +# Keep setup in sync with the one in ./docker-compose.development.yaml +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" + pull_policy: "always" + + database: + # 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 + - ./.psqlrc:/etc/postgresql-common/psqlrc:ro + environment: + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" + POSTGRES_DB: "postgres" # default database used to run `./database.mk drop` + # 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 + # 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:?required}'"] + interval: "30s" + timeout: "5s" + retries: 3 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "always" + + backend: + image: "${NAME:?required}-backend:${TARGET:?required}" + depends_on: + database: + condition: "service_started" + volumes: + - backend_logs:/app/logs + - 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 + - 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" + ASPNETCORE_ENVIRONMENT: "production" + 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__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_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__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/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", + ] + interval: "30s" + timeout: "5s" + retries: 6 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + deploy: + resources: + limits: + cpus: "0.8" + pull_policy: "never" + stdin_open: true + + frontend: + image: "${NAME:?required}-frontend:${TARGET:?required}" + depends_on: + backend: + condition: "service_started" + environment: + NODE_ENV: "production" + command: + - "node" + - "/app/server.js" + restart: "unless-stopped" + healthcheck: + test: + [ + "CMD-SHELL", + "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1", + ] + interval: "30s" + timeout: "5s" + retries: 6 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "never" + + reverse_proxy: + image: "nginx:1.31-trixie-otel" + depends_on: + backend: + condition: "service_started" + frontend: + condition: "service_started" + volumes: + - ./nginx/templates:/etc/nginx/templates:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/html:/etc/nginx/html:ro + - files:/data/files:ro + ports: + - "0.0.0.0:${HTTP_PORT:?required}:80" + command: + - "nginx" + - "-g" + - "daemon off;" + restart: "unless-stopped" + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + interval: "30s" + timeout: "5s" + retries: 3 + start_period: "60s" + start_interval: "5s" + labels: + autoheal: "true" + pull_policy: "always" + +volumes: + data: + files: + certificates: + gnupg: + backend_logs: diff --git a/docker-compose.production.yml b/docker-compose.production.yml deleted file mode 100644 index f93d4e86..00000000 --- a/docker-compose.production.yml +++ /dev/null @@ -1,128 +0,0 @@ -version: '3.3' - -services: - database: # https://hub.docker.com/_/postgres - restart: always - image: postgres:13.5-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 - # https://www.postgresql.org/docs/current/runtime-config.html - command: [ - "-c", "max_connections=100" - ] - 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 - 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 - image: rnwood/smtp4dev:v3.1 - # 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 - 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} - - 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.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 - environment: - - ASPNETCORE_URLS=http://0.0.0.0:8080 - - ASPNETCORE_ENVIRONMENT=production - - XBASE_Host=https://${HOST} - - XBASE_MetabaseHost=https://${METABASE_HOST} - - XBASE_Email__SmtpHost=${SMTP_HOST} - - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_AccessToken=${ACCESS_TOKEN} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - volumes: - - backend_logs:/app/logs - - files:/app/files - - ./secrets:/run/secrets:ro - depends_on: - - database - - email - - frontend: - 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"] - interval: 30s - timeout: 30s - retries: 3 - environment: - - NODE_ENV=production - depends_on: - - backend - - nginx: # https://hub.docker.com/_/nginx - restart: always - image: nginx:1.21 - command: [ - "nginx", - "-g", "daemon off;" - ] - healthcheck: - test: ["CMD-SHELL", "service nginx status || exit 1"] - interval: 30s - timeout: 30s - retries: 3 - ports: - - ${HTTP_PORT}: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 - depends_on: - - frontend - - backend - - email - -volumes: - data: - emails: - files: - backend_logs: diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 012f0a5d..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,187 +0,0 @@ -version: '3.3' - -services: - database: # https://hub.docker.com/_/postgres - image: postgres:13.5-bullseye - # ports: - # - 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" - ] - 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 - environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - - POSTGRES_DB=postgres - volumes: - - data:/var/lib/postgresql/data - - email: # https://hub.docker.com/r/rnwood/smtp4dev - image: rnwood/smtp4dev:v3.1 - # 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 - environment: - - ServerOptions__BasePath=/email - - ServerOptions__HostName=${HOST} - - ServerOptions__Database=database.db - - ServerOptions__NumberOfMessagesToKeep=100 - - ServerOptions__NumberOfSessionsToKeep=100 - - ServerOptions__TlsMode=None - - ServerOptions__TlsCertificate= - - backend: - build: - context: ./backend - dockerfile: ./Dockerfile - args: - - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - - CERTIFICATE_AUTHORITY_PASSWORD=${CERTIFICATE_AUTHORITY_PASSWORD} - # user: 1000:1000 - command: [ - "dotnet", "watch", - "--project", "./src/Database.csproj", - "--", - "run", - "--no-launch-profile" - ] - # "--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"] - interval: 30s - timeout: 30s - 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 - # - 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/src/bin - - /app/src/obj - - /app/test/bin - - /app/test/obj - # Create anonymous volumes for dotnet tools - - /home/me/.nuget - - /home/me/.dotnet - # ports: - # - 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;" - - ASPNETCORE_URLS=http://0.0.0.0:8080 - - ASPNETCORE_ENVIRONMENT=development - - XBASE_Host=https://${HOST}:${HTTPS_PORT} - - XBASE_MetabaseHost=https://${METABASE_HOST} - - XBASE_Email__SmtpHost=${SMTP_HOST} - - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_AccessToken=${ACCESS_TOKEN} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - - 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} - # - 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 - depends_on: - - database - - email - - frontend: - build: - context: ./frontend - dockerfile: ./Dockerfile - args: - - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - # 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 - volumes: - - ./frontend:/app - - frontend_node_modules:/app/node_modules - extra_hosts: - - ${HOST}:host-gateway - environment: - - NODE_ENV=development - depends_on: - - backend - - nginx: # https://hub.docker.com/_/nginx - image: nginx:1.21 - 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: - - ./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 - environment: - - SERVER_NAME=${HOST} - - SSL_CERTIFICATE_BASE_FILE_NAME=${SSL_CERTIFICATE_BASE_FILE_NAME} - depends_on: - - frontend - - backend - - email - -volumes: - data: - emails: - files: - backend_logs: - # backend_node_modules: - backend_vscode_server_extensions: - backend_vscode_server_insiders_extensions: - frontend_node_modules: diff --git a/docker.mk b/docker.mk new file mode 100755 index 00000000..bdd9f79b --- /dev/null +++ b/docker.mk @@ -0,0 +1,216 @@ +#!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +COMPOSE_BAKE=true +SERVICE= + +dotenv_linter = \ + docker run \ + --rm \ + --user "$(shell id --user):$(shell id --group)" \ + --volume "$(shell pwd):/mnt:ro" \ + --quiet \ + dotenvlinter/dotenv-linter:4.0.0 + +# 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 + +environment : ## Print value of variable `ENVIRONMENT` + @echo ${ENVIRONMENT} +.PHONY : 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 "./Makefile does not link to ./docker.mk" >&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}" >&2 ; \ + exit 2 ; \ + 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/.env.staging.sample /mnt/.env.production.sample +.PHONY : dotenv + +config : ## Parse, resolve and render compose file in canonical format + docker compose config ${SERVICE} +.PHONY : config + +pull : ## Pull images + docker compose pull ${SERVICE} +.PHONY : pull + +build : symlink dotenv pull ## Build images + 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 + docker compose build \ + --print \ + --pull ${SERVICE} +.PHONY : bake + +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.build-context \ + ./${SERVICE} +.PHONY : build-context + +remove : ## Remove stopped services + docker compose rm \ + --volumes ${SERVICE} +.PHONY : remove + +up : symlink dotenv ## (Re)create and start services + docker compose up \ + --no-build \ + --no-recreate \ + --remove-orphans \ + --wait ${SERVICE} +.PHONY : 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 + +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 \ + --no-build \ + --no-deps \ + --no-recreate \ + --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`) + 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 +.PHONY : prune + +logs : ## Follow logs + docker compose logs \ + --since=1h \ + --follow ${SERVICE} +.PHONY : logs + +exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service + docker compose up \ + --no-build \ + --no-recreate \ + --wait \ + ${SERVICE} + docker compose exec \ + ${SERVICE} \ + ${COMMAND} +.PHONY : exec + +enter : COMMAND = bash +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 + docker compose run \ + --rm \ + ${SERVICE} \ + ${COMMAND} +.PHONY : run + +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 services with health status + docker compose ps \ + --no-trunc \ + --all ${SERVICE} +.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 + +# 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 ?= +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 new file mode 100755 index 00000000..7aff7f1d --- /dev/null +++ b/forge.mk @@ -0,0 +1,60 @@ +#!/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 = database +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 : 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` + docker save \ + --platform "linux/amd64" \ + "${NAME}-${SERVICE}:${TARGET}" \ + | bzip2 \ + | ssh "${USER}@${HOST}" "docker load" +.PHONY : push + +all : ## Build and push all images, for example, `./forge.mk all USER=cloud HOST=192.102.162.39` + $(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.162.39` +# ssh "${USER}@${HOST}" " \ +# cd '/app/${ENVIRONMENT}' \ +# && ./deploy.mk deploy TARGET='${TARGET}' \ +# " +# .PHONY : deploy diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 14287850..662452b7 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,46 +1,24 @@ # https://docs.docker.com/engine/reference/builder/#dockerignore-file - -**/.* -!.graphql-let.yml -!.env.local -**/*.md -**/*.bak -Dockerfile* - -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -node_modules -.pnp -.pnp.js - -# testing -coverage - -# next.js -.next/ -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-let -__generated__ -*.graphql.d.ts -*.graphqls.d.ts +# The inverse dockerignore file: .dockerinclude + +* + +!. +!**/*.ts +!**/*.tsx +!**/*.graphql +!**/*.graphqls +!**/*.json +!**/*.css +!**/*.png +!public/** +!yarn.lock + +__generated__/** +**/*.generated.ts + +.next/** +next-env.d.ts/** +.vercel/** +coverage/** +node_modules/** diff --git a/frontend/.env.development b/frontend/.env.development deleted file mode 100644 index 43d84b50..00000000 --- a/frontend/.env.development +++ /dev/null @@ -1 +0,0 @@ -NODE_TLS_REJECT_UNAUTHORIZED=0 \ No newline at end of file diff --git a/frontend/.env.local.sample b/frontend/.env.local.sample deleted file mode 100644 index 15dc565d..00000000 --- a/frontend/.env.local.sample +++ /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 diff --git a/frontend/.env.test b/frontend/.env.test deleted file mode 100644 index b2783c52..00000000 --- a/frontend/.env.test +++ /dev/null @@ -1,10 +0,0 @@ -NODE_TLS_REJECT_UNAUTHORIZED=0 - -NEXT_PUBLIC_DATABASE_URL=http://localhost/ -NEXT_PUBLIC_METABASE_URL=http://localhost/ -NEXTAUTH_URL=http://localhost/ - -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/.gitignore b/frontend/.gitignore index a9737e41..67a986f0 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,5 +1,4 @@ -/secrets -/ssl +/Dockerfile # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. @@ -13,6 +12,7 @@ # next.js /.next/ +/next-env.d.ts /out/ # production @@ -36,7 +36,6 @@ yarn-error.log* # vercel .vercel -# graphql-let +# graphql-codegen __generated__ -*.graphql.d.ts -*.graphqls.d.ts +*.generated.ts diff --git a/frontend/.graphql-let.yml b/frontend/.graphql-let.yaml similarity index 63% rename from frontend/.graphql-let.yml rename to frontend/.graphql-let.yaml index c820c15c..1d11a6a5 100644 --- a/frontend/.graphql-let.yml +++ b/frontend/.graphql-let.yaml @@ -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/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 00000000..38b6a2fd --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": null, + "typescript.tsserver.experimental.enableProjectDiagnostics": true +} diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 5ce3a092..00000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,66 +0,0 @@ -# Inspired by https://mherman.org/blog/dockerizing-a-react-app/ - -# https://hub.docker.com/_/node -FROM node:16.13-buster-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 -# and does not belong to current user. Avoid it being created at all or at -# least make it belong to the current user - -############# -# 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 \ - --yes \ - --no-install-recommends \ - ca-certificates \ - dumb-init \ - make \ - openssl \ - 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 - -ENV HOME=/home/node -RUN \ - mkdir --parents ${HOME}/app && \ - chown \ - node:node \ - ${HOME}/app && \ - ln -s ${HOME}/app /app - -############# -# As `node` # -############# -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 - -ENTRYPOINT ["/usr/bin/dumb-init", "--"] diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production deleted file mode 100644 index 8920a5ce..00000000 --- a/frontend/Dockerfile-production +++ /dev/null @@ -1,37 +0,0 @@ -# Inspired by https://nextjs.org/docs/deployment#docker-image - -FROM node:16.13-buster-slim AS dependencies -WORKDIR /app -COPY ./package.json ./yarn.lock ./ -RUN yarn install --pure-lockfile - -FROM node:16.13-buster-slim AS build -WORKDIR /app -COPY ./ ./ -COPY --from=dependencies /app/node_modules ./node_modules -RUN yarn build - -FROM node:16.13-buster-slim -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/node_modules ./node_modules -COPY --from=build /app/package.json ./ -COPY --from=build /app/public ./public -RUN \ - apt-get update && \ - apt-get install \ - --yes \ - --no-install-recommends \ - wget && \ - rm \ - --recursive \ - --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 -USER nextjs diff --git a/frontend/Dockerfile.development b/frontend/Dockerfile.development new file mode 100644 index 00000000..0cf87778 --- /dev/null +++ b/frontend/Dockerfile.development @@ -0,0 +1,60 @@ +# 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.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 +# and does not belong to current user. Avoid it being created at all or at +# least make it belong to the current user + +############# +# As `root` # +############# + +# 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 \ + make \ + tini \ + wget \ + && rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + +ENV HOME=/home/node +RUN mkdir --parents "${HOME}/app" \ + && chown \ + node:node \ + "${HOME}/app" \ + && ln --symbolic "${HOME}/app" /app + +############# +# As `node` # +############# +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 \ + --frozen-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 + +ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/frontend/Dockerfile.production b/frontend/Dockerfile.production new file mode 100644 index 00000000..79107442 --- /dev/null +++ b/frontend/Dockerfile.production @@ -0,0 +1,44 @@ +# 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.16-trixie-slim AS base + +FROM base AS dependencies +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] +WORKDIR /app +COPY ./package.json ./yarn.lock ./ +RUN yarn install --frozen-lockfile + +FROM base AS build +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] +WORKDIR /app +COPY ./ ./ +COPY --from=dependencies /app/node_modules ./node_modules +RUN yarn build + +FROM base AS runtime +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] +WORKDIR /app +ENV NODE_ENV=production +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 \ + --assume-yes \ + --no-install-recommends \ + wget \ + && rm \ + --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 +USER nextjs +ENV PORT=8080 +ENV HOSTNAME="0.0.0.0" diff --git a/frontend/Makefile b/frontend/Makefile index 25085a01..fda168a0 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -1,9 +1,13 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html +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].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help @@ -13,6 +17,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 @@ -29,6 +45,20 @@ lint : ## Lint code next lint .PHONY : lint +format : ## Format the code + yarn run \ + prettier --write . +.PHONY : format + +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/README.md b/frontend/README.md deleted file mode 100644 index fa868934..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.yml` 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)). diff --git a/frontend/codegen.ts b/frontend/codegen.ts new file mode 100644 index 00000000..352775a7 --- /dev/null +++ b/frontend/codegen.ts @@ -0,0 +1,103 @@ +import type { CodegenConfig } from "@graphql-codegen/cli"; + +const scalars = { + ID: { + input: "string", + 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", + Locale: "string", + Long: "number", + NonNegativeInt: "number", + PhoneNumber: "string", + Short: "number", + TimeSpan: "string", + UnsignedByte: "number", + 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", + 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"], + 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: graphQlConfig, + }, + "./__generated__/fragmentMatcherTypes.ts": { + plugins: ["fragment-matcher"], + config: { + module: "es2015", + apolloClientVersion: 3, + useExplicitTyping: false, + deterministic: true, + }, + }, + "./__generated__/apolloHelpers.ts": { + plugins: ["typescript-apollo-client-helpers"], + }, + // './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 cb204fcf..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/ActiveFilterAndSortBar.tsx b/frontend/components/ActiveFilterAndSortBar.tsx new file mode 100644 index 00000000..5672d193 --- /dev/null +++ b/frontend/components/ActiveFilterAndSortBar.tsx @@ -0,0 +1,131 @@ +import { Tag, Space, Typography, Flex } from "antd"; +import { + ObjectFilterState, + getFilterOperatorLabel, + createFilterStateReducer, + FilterDefinition, + AnyFilterDefinition, +} from "../lib/filter"; +import { formatSortDirection, SortState, SortDefinition } from "../lib/sort"; +import DeleteButton from "./DeleteButton"; +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[], +): string => + createFilterStateReducer( + (scalar) => + `${getFilterOperatorLabel(scalar.operator)}|${JSON.stringify(scalar.value)}`, + (list, _, stringifyValue) => + `${getFilterOperatorLabel(list.operator)}|${stringifyValue()}`, + (object, context, stringifyValue) => + `${String(context.items[object.index].field)}|${stringifyValue()}`, + )(filter, definitions as AnyFilterDefinition[]); + +const renderFilter = ( + value: ObjectFilterState, + definitions: readonly FilterDefinition[], +) => + createFilterStateReducer( + (scalar) => ( + <> + {getFilterOperatorLabel(scalar.operator)}{" "} + {Array.isArray(scalar.value) + ? `{${scalar.value.join(", ")}}` + : scalar.value} + + ), + (list, _, renderValue) => ( + <> + {getFilterOperatorLabel(list.operator)} {renderValue()} + + ), + (object, context, renderValue) => ( + <> + {getLabel(context.items[object.index], "none-upper")} {renderValue()} + + ), + )(value, definitions as AnyFilterDefinition[]); + +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..614fed5b --- /dev/null +++ b/frontend/components/CodeView.tsx @@ -0,0 +1,35 @@ +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 ( + +
+          
+            {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..b97f82dc --- /dev/null +++ b/frontend/components/Copyable.tsx @@ -0,0 +1,30 @@ +import { Space } from "antd"; +import { CSSProperties, ReactNode } from "react"; +import CopyButton from "./CopyButton"; + +export default function Copyable({ + text, + onlyIcon, + color, + style, + children, +}: { + text: string; + onlyIcon?: boolean; + color?: "white"; + style?: CSSProperties; + 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..c1dd917f --- /dev/null +++ b/frontend/components/CopyableBlock.tsx @@ -0,0 +1,56 @@ +import { Button } from "antd"; +import { CheckOutlined, CopyOutlined } from "@ant-design/icons"; +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); + + return ( +
+ {children} + +
+ ); +} diff --git a/frontend/components/DateTimeX.tsx b/frontend/components/DateTimeX.tsx new file mode 100644 index 00000000..5e3a7af9 --- /dev/null +++ b/frontend/components/DateTimeX.tsx @@ -0,0 +1,38 @@ +import { Calendar, Popover } from "antd"; +import dayjs from "dayjs"; +import { Scalars } from "../__generated__/graphql"; + +type DateTimeProps = + | { + value: Scalars["DateTime"]["output"]; + } + | { + parsedValue: dayjs.Dayjs; + }; + +export default function DateTimeX(props: DateTimeProps) { + const parsedValue = "value" in props ? dayjs(props.value) : props.parsedValue; + + 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/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/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/FloatPropositionFormList.tsx b/frontend/components/FloatPropositionFormList.tsx index 229aa937..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, "comparator"]} noStyle initialValue={FloatPropositionComparator.EqualTo} > @@ -83,21 +85,19 @@ export function FloatPropositionFormList({ {...restField} key={`value${key}`} name={[name, "value"]} - fieldKey={[fieldKey, "value"]} noStyle > - remove(name)} - /> - + + ))} diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx index e5cd22bf..8505147a 100644 --- a/frontend/components/Footer.tsx +++ b/frontend/components/Footer.tsx @@ -1,17 +1,13 @@ -import { Typography } from "antd"; -import * as React from "react"; +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..7d6080df --- /dev/null +++ b/frontend/components/GnuPgKeyLink.tsx @@ -0,0 +1,26 @@ +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 d431bcc3..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..eed97db8 --- /dev/null +++ b/frontend/components/JsonView.tsx @@ -0,0 +1,39 @@ +import { Tooltip } from "antd"; +import CopyableBlock from "./CopyableBlock"; +import { CSSProperties } from "react"; + +export default function JsonView({ + data, + inline = false, + color, + style, +}: { + data: object; + inline?: boolean; + color?: "white"; + style?: CSSProperties; +}) { + 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..0e7d2fc9 --- /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 jump = (id: string | undefined) => { + 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 d729a22a..a8501103 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 { Modal, 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,47 +12,57 @@ const navItems = [ label: "Home", }, { - path: paths.data, - label: "All Data", + path: paths.allCalorimetricData, + label: "Calorimetric", }, { - path: paths.calorimetricData, - label: "Calorimetric Data", + path: paths.allGeometricData, + label: "Geometric", }, { - path: paths.hygrothermalData, - label: "Hygrothermal Data", + path: paths.allHygrothermalData, + label: "Hygrothermal", }, { - path: paths.opticalData, - label: "Optical Data", + path: paths.allLifeCycleData, + label: "Life-Cycle", }, { - path: paths.photovoltaicData, + path: paths.allOpticalData, + label: "Optical", + }, + { + path: paths.allPhotovoltaicData, label: "Photovoltaic Data", }, + { + path: paths.database, + label: "Database", + }, ]; 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]); const shouldShowCookieConsent = cookies[cookieConsentName] != cookieConsentValue; + const { modal } = App.useApp(); useEffect(() => { if (shouldShowCookieConsent) { - Modal.info({ + 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. @@ -64,23 +74,41 @@ export default function Layout({ children }: LayoutProps) { }, }); } - }, [shouldShowCookieConsent, setCookie]); + }, [shouldShowCookieConsent, setCookie, modal]); return ( - {appTitle} + {[...pageTitles, appTitle].join(" • ")} - + + + - - {children} + + +
    {children}
    +
    -