diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94bb81b740702e77ca40129c929e65b4ed23c9fe..a7dbc4c848a9b23f73354c33a0d9023f3704a623 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ stages: - test - package maven_build: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: build script: mvn --batch-mode clean compile tags: @@ -39,7 +39,7 @@ maven_build: - if: $CI_JOB_MANUAL!= "true" maven_test_rest_1: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_rest_1 tags: @@ -52,7 +52,7 @@ maven_test_rest_1: - if: $CI_JOB_MANUAL!= "true" maven_test_rest_1_ACBB: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_rest_1_ACBB tags: @@ -65,7 +65,7 @@ maven_test_rest_1_ACBB: - if: $CI_JOB_MANUAL!= "true" maven_test_rest_1_Haute_Frequence: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_rest_1_Haute_Frequence tags: @@ -78,7 +78,7 @@ maven_test_rest_1_Haute_Frequence: - if: $CI_JOB_MANUAL!= "true" maven_test_rest_2: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_rest_2 tags: @@ -91,7 +91,7 @@ maven_test_rest_2: - if: $CI_JOB_MANUAL!= "true" maven_test_rest_others: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_rest_others tags: @@ -104,7 +104,7 @@ maven_test_rest_others: - if: $CI_JOB_MANUAL!= "true" maven_test_checker: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_checker tags: @@ -117,7 +117,7 @@ maven_test_checker: - if: $CI_JOB_MANUAL!= "true" maven_test_model: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_model tags: @@ -130,7 +130,7 @@ maven_test_model: - if: $CI_JOB_MANUAL!= "true" maven_test_persistence: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: mvn --batch-mode clean test -Ptest_persistence tags: @@ -143,7 +143,7 @@ maven_test_persistence: - if: $CI_JOB_MANUAL!= "true" maven: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: package script: mvn --batch-mode clean package -Dmaven.test.skip=true tags: @@ -156,7 +156,7 @@ maven: - if: $CI_JOB_MANUAL!= "true" maven_registry: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: package script: - MVN_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) && echo $MVN_VERSION; @@ -221,7 +221,7 @@ npm: - if: $CI_JOB_MANUAL!= "true" maven_swagger_build: - image: maven:3.8.5-openjdk-17 + image: maven:3.9.4-amazoncorretto-21 stage: test script: - mvn --batch-mode clean test -Pswagger_build diff --git a/docker-compose.yml b/docker-compose.yml index 5a5f35038e496587e11632e567b27b87e90569c2..a54080aae5b98bc0d4e95f3f83c8764feedf52ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3' services: postgresql: - image: postgres:13.4 + image: postgres:16.0 ports: - "5432:5432" environment: @@ -12,7 +12,7 @@ services: networks: - default pgadmin: - image: dpage/pgadmin4 + image: dpage/pgadmin4:8.0 ports: - "8083:80" volumes: diff --git a/documentations/Documentation_fichier_Yaml/1_Introduction.md b/documentations/Documentation_fichier_Yaml/1_Introduction.md index d8e5771f02ab8230c21f13048a2d277dbf61f31d..a42a6e3683e8e8269722e9fcc9ec0a5a4adc6b7e 100644 --- a/documentations/Documentation_fichier_Yaml/1_Introduction.md +++ b/documentations/Documentation_fichier_Yaml/1_Introduction.md @@ -6,18 +6,34 @@ author: - VARLOTEAUX Lucile date: 10/10/2022 -lang: fr +lang: fr-FR numbersections: true documentclass: scrreprt +output: + pdf_document: + latex_engine: pdflatex toc: true toc-depth: 6 toc-title: "Table des matières" fontsize: 12pt +mainfont: TeX Gyre Pagella +mainfontoptions: +- Numbers=Lowercase +- Numbers=Proportional linestretch: 1 -linkcolor: black +linkcolor: blue colorlinks: true +urlstyle: sf +links-as-notes: true link-citations: true - +pagenumberinf: true +hyperrefoptions: + - linktoc=all + - pdfwindowui +hyphenation: + csv: ; +usepackege: + hyphenate: true --- # Introduction diff --git a/documentations/services_model.json b/documentations/services_model.json index 97dba7ddb95e037a9b4867a87f76199d9d500e2e..2435c6612d635e7697af2ffb36504b6a28348ff6 100644 --- a/documentations/services_model.json +++ b/documentations/services_model.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://localhost","description":"Generated server url"}],"paths":{"/api/v1/users":{"put":{"tags":["authentication-resources"],"operationId":"updateUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserResult"}}}}}},"post":{"tags":["authentication-resources"],"operationId":"createUser","parameters":[{"name":"login","in":"query","required":true,"schema":{"type":"string"}},{"name":"password","in":"query","required":true,"schema":{"type":"string"}},{"name":"email","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string","format":"uuid"}}}}}}}},"/api/v1/authorization/{role}":{"put":{"tags":["authorization-resources"],"operationId":"addAuthorization","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userIdOrLogin","in":"query","required":true,"schema":{"type":"string"}},{"name":"applicationPattern","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}},"delete":{"tags":["authorization-resources"],"operationId":"deleteAuthorization","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userIdOrLogin","in":"query","required":true,"schema":{"type":"string"}},{"name":"applicationPattern","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}}},"/api/v1/applications/{nameOrId}/synthesis/{dataType}":{"get":{"tags":["ore-si-resources"],"operationId":"getSynthesis","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"put":{"tags":["ore-si-resources"],"operationId":"buidSynthesis","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/applications/{nameOrId}/synthesis/{dataType}/{variable}":{"get":{"tags":["ore-si-resources"],"operationId":"getSynthesis_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"variable","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"put":{"tags":["ore-si-resources"],"operationId":"buidSynthesis_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"variable","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/validate-configuration":{"post":{"tags":["ore-si-resources"],"operationId":"validateConfiguration","requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/login":{"post":{"tags":["authentication-resources"],"operationId":"login","parameters":[{"name":"login","in":"query","required":true,"schema":{"type":"string"}},{"name":"password","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResult"}}}}}}},"/api/v1/applications/{name}":{"post":{"tags":["ore-si-resources"],"operationId":"createApplication","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment","in":"query","required":false,"schema":{"type":"string","default":""}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{nameOrId}/rightsRequest":{"get":{"tags":["ore-si-resources"],"description":"Get a rightsRequest with their description using search params","operationId":"listRightsRequest","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRightsRequestResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createRightsRequest","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRightsRequestRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}":{"get":{"tags":["ore-si-resources"],"operationId":"listReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetReferenceResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createReference","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"deleteReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}/references/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getReferencesAuthorizations","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationReferencesResults"}}}}}},"post":{"tags":["authorization-resources"],"operationId":"addReferenceAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReferenceAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/data/{dataType}":{"get":{"tags":["ore-si-resources"],"operationId":"getAllDataZip_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"downloadDatasetQuery","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}},"application/json":{"schema":{"$ref":"#/components/schemas/GetDataResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createData","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"deleteData","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"downloadDatasetQuery","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}/configuration":{"get":{"tags":["ore-si-resources"],"operationId":"getConfiguration","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"string","format":"byte"}}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"changeConfiguration","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment","in":"query","required":false,"schema":{"type":"string","default":""}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{nameOrId}/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizations_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationResults"}}}}}},"post":{"tags":["authorization-resources"],"operationId":"addAuthorization_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles/{additionalFileName}":{"get":{"tags":["ore-si-resources"],"operationId":"listAdditionalFilesNames","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"additionalFileName","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAdditionalFilesResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createAdditionalFile","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"additionalFileName","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string","format":"uuid"}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles/authorization":{"post":{"tags":["authorization-resources"],"operationId":"addAdditionalFileAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdditionalFileAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/users/{userLoginOrId}":{"get":{"tags":["authentication-resources"],"operationId":"getByIdOrLogin","parameters":[{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}}},"/api/v1/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizations","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LoginResult"}}}}}}}},"/api/v1/applications":{"get":{"tags":["ore-si-resources"],"operationId":"getApplications","parameters":[{"name":"filter","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[]}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{name}/file/{id}":{"get":{"tags":["ore-si-resources"],"operationId":"getFile","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"string","format":"byte"}}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"removeFile","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}":{"get":{"tags":["ore-si-resources"],"operationId":"getApplication","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"filter","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[]}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationResult"}}}}}}},"/api/v1/applications/{nameOrId}/references":{"get":{"tags":["ore-si-resources"],"operationId":"listNameReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}/{column}":{"get":{"tags":["ore-si-resources"],"operationId":"listReferences_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"column","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"array","items":{"type":"string"}}}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}/csv":{"get":{"tags":["ore-si-resources"],"operationId":"listReferencesCsv","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{nameOrId}/grantable":{"get":{"tags":["authorization-resources"],"operationId":"getGrantable","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetGrantableResult"}}}}}}},"/api/v1/applications/{nameOrId}/filesOnRepository/{dataType}":{"get":{"tags":["ore-si-resources"],"operationId":"getFilesOnRepository","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"repositoryId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BinaryFile"}}}}}}}},"/api/v1/applications/{nameOrId}/data":{"get":{"tags":["ore-si-resources"],"operationId":"listDataType","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/authorization/{authorizationId}":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizationById","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationResult"}}}}}},"delete":{"tags":["authorization-resources"],"operationId":"revokeAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string","format":"uuid"}}}}}}},"/api/v1/applications/{nameOrId}/additionalfiles/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAdditionalFilesAuthorizations","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationAdditionalFilesResults"}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles":{"get":{"tags":["ore-si-resources"],"summary":"Returns a zip containing additional files and their description","description":"Get a additionalFiles with their description using search params","operationId":"getAdditionalFilesNamesZip","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}},"delete":{"tags":["ore-si-resources"],"summary":"Delete additional file based on params search","description":"Delete a additionalFiles ","operationId":"removeAdditionalFiles","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/references/authorization/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getReferencesAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsReferencesResult"}}}}}}},"/api/v1/applications/{applicationNameOrId}/authorization/user/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsResult"}}}}}}},"/api/v1/applications/{applicationNameOrId}/additionalFiles/authorization/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getAdditionalFilesAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsAdditionalFilesResult"}}}}}}},"/":{"get":{"tags":["home-resources"],"operationId":"home","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/hal+json":{"schema":{"$ref":"#/components/schemas/RedirectView"}}}}}}},"/api/v1/logout":{"delete":{"tags":["authentication-resources"],"operationId":"logout","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/hal+json":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/references/authorization/{authorizationId}":{"delete":{"tags":["authorization-resources"],"operationId":"revokeReferencesAuthorization","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/additionalFiles/authorization/{authorizationId}":{"delete":{"tags":["authorization-resources"],"operationId":"revokeAdditionalFilesAuthorization","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"ObjectError":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"string"}},"arguments":{"type":"array","items":{"type":"object"}},"defaultMessage":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"}}},"CreateUserRequest":{"type":"object","properties":{"login":{"type":"string"},"password":{"type":"string"},"email":{"type":"string"},"newPassword":{"type":"string"},"newPasswordConfirm":{"type":"string"},"verificationKey":{"type":"string"},"charte":{"type":"string"}}},"CreateUserResult":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"login":{"type":"string"},"email":{"type":"string"},"accountState":{"type":"string","enum":["idle","active","pending","closed"]},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"OreSiUser":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"creationDate":{"type":"string","format":"date-time"},"updateDate":{"type":"string","format":"date-time"},"login":{"type":"string"},"password":{"type":"string"},"email":{"type":"string"},"authorizations":{"type":"array","items":{"type":"string"}},"accountstate":{"type":"string","enum":["idle","active","pending","closed"]},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"StreamingResponseBody":{"type":"object"},"LoginResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"login":{"type":"string"},"email":{"type":"string"},"state":{"type":"string"},"authorizedForApplicationCreation":{"type":"boolean"},"superadmin":{"type":"boolean"},"authorizations":{"type":"array","items":{"type":"string"}},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"Authorization":{"type":"object","properties":{"timeScope":{"$ref":"#/components/schemas/LocalDateTimeRange"},"dataGroups":{"type":"array","items":{"type":"string"}},"requiredAuthorizations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Ltree"}},"intervalDates":{"type":"object","additionalProperties":{"type":"string","format":"date"},"writeOnly":true}}},"CreateAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}}}},"CreateRightsRequestRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"fields":{"type":"object","additionalProperties":{"type":"string"}},"rightsRequest":{"$ref":"#/components/schemas/CreateAuthorizationRequest"},"setted":{"type":"boolean"},"comment":{"type":"string"}}},"LocalDateTimeRange":{"type":"object","properties":{"range":{"$ref":"#/components/schemas/RangeLocalDateTime"}}},"Ltree":{"type":"object","properties":{"sql":{"type":"string"}}},"RangeLocalDateTime":{"type":"object","properties":{"empty":{"type":"boolean"}}},"CreateReferenceAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"references":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"CreateAdditionalFileAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"additionalFiles":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"AddVariableMigrationDescription":{"type":"object","properties":{"defaultValue":{"type":"string"}}},"AdditionalFile":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string"}}}},"AdditionalFileDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFileFieldFormat"}}}},"AdditionalFileFieldFormat":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"}}},"ApplicationDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"defaultLanguage":{"type":"object","properties":{"language":{"type":"string"},"script":{"type":"string"},"country":{"type":"string"},"variant":{"type":"string"},"unicodeLocaleAttributes":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"unicodeLocaleKeys":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"displayLanguage":{"type":"string"},"displayScript":{"type":"string"},"displayCountry":{"type":"string"},"displayVariant":{"type":"string"},"displayName":{"type":"string"},"extensionKeys":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"iso3Language":{"type":"string"},"iso3Country":{"type":"string"}}},"internationalization":{"$ref":"#/components/schemas/InternationalizationApplicationMap"}}},"ApplicationResult":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"},"comment":{"type":"string"},"internationalization":{"$ref":"#/components/schemas/InternationalizationMap"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Reference"}},"authorizationReferencesRights":{"$ref":"#/components/schemas/AuthorizationsForUserResult"},"referenceSynthesis":{"type":"array","items":{"$ref":"#/components/schemas/ReferenceSynthesis"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataType"}},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFile"}},"authorizationsDatatypesRights":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"rightsRequest":{"$ref":"#/components/schemas/RightsRequest"},"configuration":{"$ref":"#/components/schemas/Configuration"},"isAdministrator":{"type":"boolean"}}},"AuthorizationColumnsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"display":{"type":"boolean"},"title":{"type":"string"},"withPeriods":{"type":"boolean"},"withDataGroups":{"type":"boolean"},"forPublic":{"type":"boolean"},"forRequest":{"type":"boolean"}}},"AuthorizationDescription":{"type":"object","properties":{"timeScope":{"$ref":"#/components/schemas/VariableComponentKey"},"authorizationScopes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AuthorizationScopeDescription"}},"dataGroups":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataGroupDescription"}},"columnsDescription":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AuthorizationColumnsDescription"}},"internationalization":{"$ref":"#/components/schemas/InternationalizationAuthorisationMap"}}},"AuthorizationScopeDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"variable":{"type":"string"},"component":{"type":"string"},"variableComponentKey":{"$ref":"#/components/schemas/VariableComponentKey"}}},"AuthorizationsForUserResult":{"type":"object","properties":{"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"},"userId":{"type":"string"}}},"Chart":{"type":"object","properties":{"value":{"type":"string"},"aggregation":{"$ref":"#/components/schemas/VariableComponentKey"},"unit":{"type":"string"},"gap":{"type":"string"},"standardDeviation":{"type":"string"}}},"CheckerConfigurationDescription":{"type":"object","properties":{"pattern":{"type":"string"},"refType":{"type":"string"},"groovy":{"$ref":"#/components/schemas/GroovyConfiguration"},"duration":{"type":"string"},"min":{"type":"string"},"max":{"type":"string"},"transformation":{"$ref":"#/components/schemas/TransformationConfigurationDescription"},"required":{"type":"boolean"},"multiplicity":{"type":"string","enum":["ONE","MANY"]}}},"CheckerDescription":{"type":"object","properties":{"name":{"type":"string","enum":["Reference","Boolean","Date","Integer","Float","String","GroovyExpression"]},"params":{"$ref":"#/components/schemas/CheckerConfigurationDescription"}}},"Column":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"key":{"type":"boolean"},"linkedTo":{"type":"string"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"ColumnBindingDescription":{"type":"object","properties":{"header":{"type":"string"},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]}}},"Component":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"CompositeReferenceComponentDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"reference":{"type":"string"},"parentKeyColumn":{"type":"string"},"parentRecursiveKey":{"type":"string"}}},"CompositeReferenceDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"components":{"type":"array","items":{"$ref":"#/components/schemas/CompositeReferenceComponentDescription"}}}},"ComputedVariableComponentDescription":{"type":"object","properties":{"checker":{"$ref":"#/components/schemas/CheckerDescription"},"tags":{"type":"array","items":{"type":"string"}},"computation":{"$ref":"#/components/schemas/GroovyConfiguration"},"hidden":{"type":"boolean"}}},"Configuration":{"type":"object","properties":{"requiredAuthorizationsAttributes":{"type":"array","items":{"type":"string"}},"version":{"type":"integer","format":"int32"},"internationalization":{"$ref":"#/components/schemas/InternationalizationMap"},"comment":{"type":"string"},"application":{"$ref":"#/components/schemas/ApplicationDescription"},"tags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"rightsRequest":{"$ref":"#/components/schemas/RightsRequestDescription"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceDescription"}},"compositeReferences":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/CompositeReferenceDescription"}},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFileDescription"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataTypeDescription"}}}},"DataGroupDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"label":{"type":"string"},"data":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"DataType":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"variables":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Variable"}},"repository":{"$ref":"#/components/schemas/Repository"},"hasAuthorizations":{"type":"boolean"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"DataTypeDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplays":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDisplay"}},"format":{"$ref":"#/components/schemas/FormatDescription"},"data":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/VariableDescription"}},"validations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineValidationRuleWithVariableComponentsDescription"}},"uniqueness":{"type":"array","items":{"$ref":"#/components/schemas/VariableComponentKey"}},"migrations":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/MigrationDescription"}}},"authorization":{"$ref":"#/components/schemas/AuthorizationDescription"},"repository":{"$ref":"#/components/schemas/RepositoryDescription"},"tags":{"type":"array","items":{"type":"string"}}}},"DynamicColumn":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"headerPrefix":{"type":"string"},"reference":{"type":"string"},"referenceColumnToLookForHeader":{"type":"string"},"presenceConstraint":{"type":"boolean"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"FieldFormat":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"}}},"FormatDescription":{"type":"object","properties":{"headerLine":{"type":"integer","format":"int32"},"firstRowLine":{"type":"integer","format":"int32"},"separator":{"type":"string"},"columns":{"type":"array","items":{"$ref":"#/components/schemas/ColumnBindingDescription"}},"repeatedColumns":{"type":"array","items":{"$ref":"#/components/schemas/RepeatedColumnBindingDescription"}},"constants":{"type":"array","items":{"$ref":"#/components/schemas/HeaderConstantDescription"}},"allowUnexpectedColumns":{"type":"boolean"}}},"GroovyConfiguration":{"type":"object","properties":{"expression":{"type":"string"},"references":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"datatypes":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"HeaderConstantDescription":{"type":"object","properties":{"rowNumber":{"type":"integer","format":"int32"},"columnNumber":{"type":"integer","format":"int32"},"headerName":{"type":"string"},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"exportHeader":{"type":"string"}}},"HeaderPatternToken":{"type":"object","properties":{"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"exportHeader":{"type":"string"}}},"Internationalization":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"InternationalizationAdditonalFilesMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationApplicationMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"InternationalizationAuthorisationMap":{"type":"object","properties":{"dataGroups":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}},"authorizationScopes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}},"columnsDescription":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}}}},"InternationalizationAuthorisationName":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"InternationalizationDataTypeMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"authorization":{"$ref":"#/components/schemas/InternationalizationAuthorisationMap"},"internationalizationDisplay":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDisplay"}},"internationalizedValidations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationDisplay":{"type":"object","properties":{"pattern":{"type":"object","additionalProperties":{"type":"string"}}}},"InternationalizationMap":{"type":"object","properties":{"application":{"$ref":"#/components/schemas/InternationalizationApplicationMap"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationReferenceMap"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDataTypeMap"}},"internationalizedTags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"rightsRequest":{"$ref":"#/components/schemas/InternationalizationRightsRequestMap"},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAdditonalFilesMap"}}}},"InternationalizationReferenceMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizedDynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedValidations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizedTags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationRightsRequestMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"description":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"LineValidationRuleWithColumnsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"columns":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"LineValidationRuleWithVariableComponentsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"variableComponents":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/VariableComponentKey"}}}},"MigrationDescription":{"type":"object","properties":{"strategy":{"type":"string","enum":["ADD_VARIABLE"]},"dataGroup":{"type":"string"},"variable":{"type":"string"},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AddVariableMigrationDescription"}}}},"Reference":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"children":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"columns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Column"}},"dynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DynamicColumn"}},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"ReferenceDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"separator":{"type":"string"},"keyColumns":{"type":"array","items":{"type":"string"}},"columns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceStaticNotComputedColumnDescription"}},"computedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceStaticComputedColumnDescription"}},"dynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceDynamicColumnDescription"}},"validations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineValidationRuleWithColumnsDescription"}},"allowUnexpectedColumns":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}}}},"ReferenceDynamicColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"headerPrefix":{"type":"string"},"reference":{"type":"string"},"referenceColumnToLookForHeader":{"type":"string"}}},"ReferenceStaticComputedColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"headerName":{"type":"string"},"computation":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"ReferenceStaticNotComputedColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"headerName":{"type":"string"},"defaultValue":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"ReferenceSynthesis":{"type":"object","properties":{"ReferenceType":{"type":"string"},"lineCount":{"type":"integer","format":"int32"},"referenceType":{"type":"string"}}},"RepeatedColumnBindingDescription":{"type":"object","properties":{"headerPattern":{"type":"string"},"exportHeader":{"type":"string"},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/HeaderPatternToken"}},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"}}},"Repository":{"type":"object","properties":{"filePattern":{"type":"string"},"authorizationScope":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"startDate":{"$ref":"#/components/schemas/TokenDateDescription"},"endDate":{"$ref":"#/components/schemas/TokenDateDescription"}}},"RepositoryDescription":{"type":"object","properties":{"filePattern":{"type":"string"},"authorizationScope":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"startDate":{"$ref":"#/components/schemas/TokenDateDescription"},"endDate":{"$ref":"#/components/schemas/TokenDateDescription"}}},"RightsRequest":{"type":"object","properties":{"description":{"$ref":"#/components/schemas/RightsRequestDescription"}}},"RightsRequestDescription":{"type":"object","properties":{"description":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FieldFormat"}}}},"TokenDateDescription":{"type":"object","properties":{"token":{"type":"integer","format":"int32"}}},"TransformationConfigurationDescription":{"type":"object","properties":{"codify":{"type":"boolean"},"groovy":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"Variable":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Component"}},"chartDescription":{"$ref":"#/components/schemas/Chart"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"VariableComponentKey":{"type":"object","properties":{"variable":{"type":"string"},"component":{"type":"string"},"id":{"type":"string"}}},"VariableComponentWithDefaultValueDescription":{"type":"object","properties":{"checker":{"$ref":"#/components/schemas/CheckerDescription"},"tags":{"type":"array","items":{"type":"string"}},"defaultValue":{"$ref":"#/components/schemas/GroovyConfiguration"},"hidden":{"type":"boolean"}}},"VariableDescription":{"type":"object","properties":{"chartDescription":{"$ref":"#/components/schemas/Chart"},"tags":{"type":"array","items":{"type":"string"}},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/VariableComponentWithDefaultValueDescription"}},"computedComponents":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ComputedVariableComponentDescription"}},"hidden":{"type":"boolean"}}},"AuthorizationParsed":{"type":"object","properties":{"path":{"type":"string"},"dataGroups":{"type":"array","items":{"type":"string"}},"requiredAuthorizations":{"type":"object","additionalProperties":{"type":"string"}},"fromDay":{"type":"string","format":"date"},"toDay":{"type":"string","format":"date"}}},"GetRightsRequestResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"rightsRequests":{"type":"array","items":{"$ref":"#/components/schemas/RightsRequestResult"}},"description":{"$ref":"#/components/schemas/RightsRequestDescription"}}},"RightsRequestResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"application":{"type":"string","format":"uuid"},"user":{"type":"string","format":"uuid"},"comment":{"type":"string"},"rightsRequestForm":{"type":"object","additionalProperties":{"type":"string"}},"rightsRequest":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"authorizationByDatatypeAndPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"setted":{"type":"boolean"}}},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"}}},"MultiValueMapStringString":{"type":"object","properties":{"all":{"type":"object","additionalProperties":{"type":"string"},"writeOnly":true},"empty":{"type":"boolean"}},"additionalProperties":{"type":"array","items":{"type":"string"}}},"GetReferenceResult":{"type":"object","properties":{"referenceValues":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/ReferenceValue"}},"referenceTypeForReferencingColumns":{"type":"object","additionalProperties":{"type":"string"}}}},"ReferenceValue":{"type":"object","properties":{"id":{"type":"string"},"hierarchicalKey":{"type":"string"},"hierarchicalReference":{"type":"string"},"naturalKey":{"type":"string"},"values":{"type":"object"},"refsLinkedTo":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}}},"referencingReference":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"AuthorizationsReferencesResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"}}},"GetAuthorizationReferencesResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"GetAuthorizationReferencesResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationReferencesResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsReferencesResult"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}}}},"AuthorizationScope":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"options":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/Option"}}}},"AuthorizationsResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"applicationName":{"type":"string"},"authorizationByPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"isAdministrator":{"type":"boolean"}}},"ColumnDescription":{"type":"object","properties":{"display":{"type":"boolean"},"title":{"type":"string"},"withPeriods":{"type":"boolean"},"withDataGroups":{"type":"boolean"},"forPublic":{"type":"boolean"},"forRequest":{"type":"boolean"},"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"DataGroup":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"GetGrantableResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"dataGroups":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/DataGroup"}}},"authorizationScopes":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/AuthorizationScope"}}},"columnsDescription":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ColumnDescription"}}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"},"publicAuthorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}}}},"Option":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"BinaryFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"creationDate":{"type":"string","format":"date-time"},"updateDate":{"type":"string","format":"date-time"},"application":{"type":"string","format":"uuid"},"name":{"type":"string"},"comment":{"type":"string"},"size":{"type":"integer","format":"int64"},"data":{"type":"array","items":{"type":"string","format":"byte"}},"params":{"$ref":"#/components/schemas/BinaryFileInfos"}}},"BinaryFileDataset":{"type":"object","properties":{"datatype":{"type":"string"},"requiredAuthorizations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Ltree"}},"from":{"type":"string"},"to":{"type":"string"},"comment":{"type":"string"}}},"BinaryFileInfos":{"type":"object","properties":{"binaryFiledataset":{"$ref":"#/components/schemas/BinaryFileDataset"},"published":{"type":"boolean"},"publisheduser":{"type":"string","format":"uuid"},"publisheddate":{"type":"string"},"createuser":{"type":"string","format":"uuid"},"createdate":{"type":"string"},"comment":{"type":"string"}}},"CheckerTarget":{"type":"object"},"DataRowResult":{"type":"object","properties":{"rowId":{"type":"string"},"values":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object"}}},"refsLinkedTo":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}}}},"totalRows":{"type":"integer","format":"int64"},"rowNumber":{"type":"integer","format":"int64"}}},"FieldType":{"type":"object","properties":{"value":{"type":"object"},"sqlType":{"type":"string","enum":["UUID","LTREE","TEXT","INTEGER","NUMERIC","COMPOSITE_DATE","BOOLEAN","JSONB"]}}},"GetDataResult":{"type":"object","properties":{"variables":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/DataRowResult"}},"totalRows":{"type":"integer","format":"int64"},"checkedFormatVariableComponents":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineCheckerWarper"}}}}},"LineCheckerWarper":{"type":"object","properties":{"checkerDescription":{"$ref":"#/components/schemas/CheckerDescription"},"target":{"$ref":"#/components/schemas/CheckerTarget"},"fieldType":{"$ref":"#/components/schemas/FieldType"},"transformer":{"$ref":"#/components/schemas/LineTransformer"},"sqlType":{"type":"string","enum":["UUID","LTREE","TEXT","INTEGER","NUMERIC","COMPOSITE_DATE","BOOLEAN","JSONB"]},"multiplicity":{"type":"string","enum":["ONE","MANY"]},"underlyingType":{"$ref":"#/components/schemas/FieldType"}}},"LineTransformer":{"type":"object"},"GetAuthorizationResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"publicAuthorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"}}},"GetAuthorizationResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"}}},"AuthorizationsAdditionalFilesResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"}}},"GetAuthorizationAdditionalFilesResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"GetAuthorizationAdditionalFilesResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationAdditionalFilesResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsAdditionalFilesResult"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}}}},"AdditionalBinaryFileResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"application":{"type":"string","format":"uuid"},"user":{"type":"string","format":"uuid"},"updateUser":{"type":"string","format":"uuid"},"additionalBinaryFileType":{"type":"string"},"comment":{"type":"string"},"fileName":{"type":"string"},"fileType":{"type":"string"},"size":{"type":"integer","format":"int64"},"additionalBinaryFileForm":{"type":"object","additionalProperties":{"type":"string"}},"associates":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"associatesByDatatypeAndPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"updateDate":{"type":"string","format":"date-time"},"forApplication":{"type":"boolean"}}},"GetAdditionalFilesResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"additionalFileName":{"type":"string"},"additionalBinaryFiles":{"type":"array","items":{"$ref":"#/components/schemas/AdditionalBinaryFileResult"}},"description":{"$ref":"#/components/schemas/AdditionalFileDescription"},"fileNames":{"type":"array","items":{"type":"string"}}}},"ApplicationContext":{"type":"object","properties":{"parent":{"$ref":"#/components/schemas/ApplicationContext"},"id":{"type":"string"},"displayName":{"type":"string"},"applicationName":{"type":"string"},"autowireCapableBeanFactory":{"$ref":"#/components/schemas/AutowireCapableBeanFactory"},"startupDate":{"type":"integer","format":"int64"},"environment":{"$ref":"#/components/schemas/Environment"},"beanDefinitionCount":{"type":"integer","format":"int32"},"beanDefinitionNames":{"type":"array","items":{"type":"string"}},"parentBeanFactory":{"$ref":"#/components/schemas/BeanFactory"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"parent":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"unnamedModule":{"type":"object","properties":{"name":{"type":"string"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"descriptor":{"type":"object","properties":{"open":{"type":"boolean"},"automatic":{"type":"boolean"}}},"named":{"type":"boolean"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"packages":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"layer":{"type":"object"}}},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"unnamedModule":{"type":"object","properties":{"name":{"type":"string"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"descriptor":{"type":"object","properties":{"open":{"type":"boolean"},"automatic":{"type":"boolean"}}},"named":{"type":"boolean"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"packages":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"layer":{"type":"object"}}},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}}}},"AutowireCapableBeanFactory":{"type":"object"},"BeanFactory":{"type":"object"},"Environment":{"type":"object","properties":{"activeProfiles":{"type":"array","items":{"type":"string"}},"defaultProfiles":{"type":"array","items":{"type":"string"}}}},"FilterRegistration":{"type":"object","properties":{"servletNameMappings":{"type":"array","items":{"type":"string"}},"urlPatternMappings":{"type":"array","items":{"type":"string"}},"initParameters":{"type":"object","additionalProperties":{"type":"string"}},"name":{"type":"string"},"className":{"type":"string"}}},"HttpStatusCode":{"type":"object","properties":{"is5xxServerError":{"type":"boolean"},"error":{"type":"boolean"},"is2xxSuccessful":{"type":"boolean"},"is4xxClientError":{"type":"boolean"},"is1xxInformational":{"type":"boolean"},"is3xxRedirection":{"type":"boolean"}}},"JspConfigDescriptor":{"type":"object","properties":{"taglibs":{"type":"array","items":{"$ref":"#/components/schemas/TaglibDescriptor"}},"jspPropertyGroups":{"type":"array","items":{"$ref":"#/components/schemas/JspPropertyGroupDescriptor"}}}},"JspPropertyGroupDescriptor":{"type":"object","properties":{"defaultContentType":{"type":"string"},"buffer":{"type":"string"},"urlPatterns":{"type":"array","items":{"type":"string"}},"elIgnored":{"type":"string"},"errorOnELNotFound":{"type":"string"},"pageEncoding":{"type":"string"},"scriptingInvalid":{"type":"string"},"isXml":{"type":"string"},"includePreludes":{"type":"array","items":{"type":"string"}},"includeCodas":{"type":"array","items":{"type":"string"}},"deferredSyntaxAllowedAsLiteral":{"type":"string"},"trimDirectiveWhitespaces":{"type":"string"},"errorOnUndeclaredNamespace":{"type":"string"}}},"RedirectView":{"type":"object","properties":{"applicationContext":{"$ref":"#/components/schemas/ApplicationContext"},"servletContext":{"$ref":"#/components/schemas/ServletContext"},"contentType":{"type":"string"},"requestContextAttribute":{"type":"string"},"staticAttributes":{"type":"object","additionalProperties":{"type":"object"}},"exposePathVariables":{"type":"boolean"},"exposeContextBeansAsAttributes":{"type":"boolean","writeOnly":true},"exposedContextBeanNames":{"type":"array","writeOnly":true,"items":{"type":"string"}},"beanName":{"type":"string"},"url":{"type":"string"},"contextRelative":{"type":"boolean","writeOnly":true},"http10Compatible":{"type":"boolean","writeOnly":true},"exposeModelAttributes":{"type":"boolean","writeOnly":true},"encodingScheme":{"type":"string","writeOnly":true},"statusCode":{"$ref":"#/components/schemas/HttpStatusCode"},"expandUriTemplateVariables":{"type":"boolean","writeOnly":true},"propagateQueryParams":{"type":"boolean","writeOnly":true},"hosts":{"type":"array","items":{"type":"string"}},"propagateQueryProperties":{"type":"boolean"},"redirectView":{"type":"boolean"},"attributes":{"type":"object","additionalProperties":{"type":"string"},"writeOnly":true},"attributesCSV":{"type":"string","writeOnly":true},"attributesMap":{"type":"object","additionalProperties":{"type":"object"}}}},"ServletContext":{"type":"object","properties":{"majorVersion":{"type":"integer","format":"int32"},"minorVersion":{"type":"integer","format":"int32"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"attributeNames":{"type":"object"},"jspConfigDescriptor":{"$ref":"#/components/schemas/JspConfigDescriptor"},"effectiveMajorVersion":{"type":"integer","format":"int32"},"effectiveMinorVersion":{"type":"integer","format":"int32"},"serverInfo":{"type":"string"},"servletContextName":{"type":"string"},"servletRegistrations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ServletRegistration"}},"filterRegistrations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FilterRegistration"}},"sessionCookieConfig":{"$ref":"#/components/schemas/SessionCookieConfig"},"sessionTrackingModes":{"uniqueItems":true,"type":"array","writeOnly":true,"items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"defaultSessionTrackingModes":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"effectiveSessionTrackingModes":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"virtualServerName":{"type":"string"},"sessionTimeout":{"type":"integer","format":"int32"},"requestCharacterEncoding":{"type":"string"},"responseCharacterEncoding":{"type":"string"},"contextPath":{"type":"string"},"initParameterNames":{"type":"object"}}},"ServletRegistration":{"type":"object","properties":{"runAsRole":{"type":"string"},"mappings":{"type":"array","items":{"type":"string"}},"initParameters":{"type":"object","additionalProperties":{"type":"string"}},"name":{"type":"string"},"className":{"type":"string"}}},"SessionCookieConfig":{"type":"object","properties":{"comment":{"type":"string","deprecated":true},"name":{"type":"string"},"path":{"type":"string"},"attributes":{"type":"object","additionalProperties":{"type":"string"}},"domain":{"type":"string"},"maxAge":{"type":"integer","format":"int32"},"secure":{"type":"boolean"},"httpOnly":{"type":"boolean"}}},"TaglibDescriptor":{"type":"object","properties":{"taglibURI":{"type":"string"},"taglibLocation":{"type":"string"}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"title":"OpenAPI definition","version":"v0"},"servers":[{"url":"http://localhost","description":"Generated server url"}],"paths":{"/api/v1/users":{"put":{"tags":["authentication-resources"],"operationId":"updateUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserResult"}}}}}},"post":{"tags":["authentication-resources"],"operationId":"createUser","parameters":[{"name":"login","in":"query","required":true,"schema":{"type":"string"}},{"name":"password","in":"query","required":true,"schema":{"type":"string"}},{"name":"email","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string","format":"uuid"}}}}}}}},"/api/v1/authorization/{role}":{"put":{"tags":["authorization-resources"],"operationId":"addAuthorization","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userIdOrLogin","in":"query","required":true,"schema":{"type":"string"}},{"name":"applicationPattern","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}},"delete":{"tags":["authorization-resources"],"operationId":"deleteAuthorization","parameters":[{"name":"role","in":"path","required":true,"schema":{"type":"string"}},{"name":"userIdOrLogin","in":"query","required":true,"schema":{"type":"string"}},{"name":"applicationPattern","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}}},"/api/v1/applications/{nameOrId}/synthesis/{dataType}":{"get":{"tags":["ore-si-resources"],"operationId":"getSynthesis","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"put":{"tags":["ore-si-resources"],"operationId":"buidSynthesis","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/applications/{nameOrId}/synthesis/{dataType}/{variable}":{"get":{"tags":["ore-si-resources"],"operationId":"getSynthesis_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"variable","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"put":{"tags":["ore-si-resources"],"operationId":"buidSynthesis_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"variable","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/validate-configuration":{"post":{"tags":["ore-si-resources"],"operationId":"validateConfiguration","requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReactiveResultObject"}}}}}}}},"/api/v1/login":{"post":{"tags":["authentication-resources"],"operationId":"login","parameters":[{"name":"login","in":"query","required":true,"schema":{"type":"string"}},{"name":"password","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResult"}}}}}}},"/api/v1/applications/{name}":{"post":{"tags":["ore-si-resources"],"operationId":"createApplication","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment","in":"query","required":false,"schema":{"type":"string","default":""}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReactiveResultObject"}}}}}}}},"/api/v1/applications/{nameOrId}/rightsRequest":{"get":{"tags":["ore-si-resources"],"description":"Get a rightsRequest with their description using search params","operationId":"listRightsRequest","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRightsRequestResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createRightsRequest","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRightsRequestRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}":{"get":{"tags":["ore-si-resources"],"operationId":"listReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetReferenceResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createReference","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"object"}}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"deleteReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}/references/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getReferencesAuthorizations","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationReferencesResults"}}}}}},"post":{"tags":["authorization-resources"],"operationId":"addReferenceAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReferenceAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/data/{dataType}":{"post":{"tags":["ore-si-resources"],"operationId":"createData","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"deleteData","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"downloadDatasetQuery","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}/configuration":{"get":{"tags":["ore-si-resources"],"operationId":"getConfiguration","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"string","format":"byte"}}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"changeConfiguration","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment","in":"query","required":false,"schema":{"type":"string","default":""}}],"requestBody":{"content":{"application/json":{"schema":{"required":["file"],"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReactiveResultObject"}}}}}}}},"/api/v1/applications/{nameOrId}/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizations_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationResults"}}}}}},"post":{"tags":["authorization-resources"],"operationId":"addAuthorization_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles/{additionalFileName}":{"get":{"tags":["ore-si-resources"],"operationId":"listAdditionalFilesNames","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"additionalFileName","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAdditionalFilesResult"}}}}}},"post":{"tags":["ore-si-resources"],"operationId":"createAdditionalFile","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"additionalFileName","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}}}}}},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string","format":"uuid"}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles/authorization":{"post":{"tags":["authorization-resources"],"operationId":"addAdditionalFileAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAdditionalFileAuthorizationRequest"}}},"required":true},"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/api/v1/users/{userLoginOrId}":{"get":{"tags":["authentication-resources"],"operationId":"getByIdOrLogin","parameters":[{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OreSiUser"}}}}}}},"/api/v1/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizations","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LoginResult"}}}}}}}},"/api/v1/applications":{"get":{"tags":["ore-si-resources"],"operationId":"getApplications","parameters":[{"name":"filter","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[]}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/x-ndjson":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ReactiveResultObject"}}}}}}}},"/api/v1/applications/{name}/file/{id}":{"get":{"tags":["ore-si-resources"],"operationId":"getFile","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"type":"array","items":{"type":"string","format":"byte"}}}}}}},"delete":{"tags":["ore-si-resources"],"operationId":"removeFile","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{nameOrId}":{"get":{"tags":["ore-si-resources"],"operationId":"getApplication","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"filter","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"default":[]}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationResult"}}}}}}},"/api/v1/applications/{nameOrId}/references":{"get":{"tags":["ore-si-resources"],"operationId":"listNameReferences","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}/{column}":{"get":{"tags":["ore-si-resources"],"operationId":"listReferences_1","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"column","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"array","items":{"type":"string"}}}}}}}}},"/api/v1/applications/{nameOrId}/references/{refType}/csv":{"get":{"tags":["ore-si-resources"],"operationId":"listReferencesCsv","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"refType","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{nameOrId}/grantable":{"get":{"tags":["authorization-resources"],"operationId":"getGrantable","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetGrantableResult"}}}}}}},"/api/v1/applications/{nameOrId}/filesOnRepository/{dataType}":{"get":{"tags":["ore-si-resources"],"operationId":"getFilesOnRepository","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"repositoryId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BinaryFile"}}}}}}}},"/api/v1/applications/{nameOrId}/data":{"get":{"tags":["ore-si-resources"],"operationId":"listDataType","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}}}}},"/api/v1/applications/{nameOrId}/data/{dataType}/zip":{"get":{"tags":["ore-si-resources"],"operationId":"getAllDataZip","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}},{"name":"downloadDatasetQuery","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}}},"/api/v1/applications/{nameOrId}/data/{dataType}/json":{"get":{"tags":["ore-si-resources"],"description":"Return an extraction of data of datatType 'dataType' of application 'nameOrId'","operationId":"getAllDataJson","parameters":[{"name":"downloadDatasetQuery","in":"query","description":"An objetc for reduce, filter and order result","required":false,"$ref":"fr.inra.oresing.persistence.requestBuilder.datatype.DownloadDatasetQuery","schema":{"type":"string"},"examples":{"select by authorization":{"description":" {\n \"authorizationDescriptions\": [\n {\n \"timeScope\": {\n \"from\": \"1984-01-01\",\n \"to\": \"1984-01-03\"\n },\n \"requiredAuthorizations\": [\n {\n \"projet\": \"projet_manche\",\n \"localization\": \"plateforme\"\n }\n ]\n }\n ]\n }\n","value":"Find by authorizations. You can provide an optional json with authorization","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"select filter by filter (like '%filter%')":{"description":" {\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"site\",\n \"component\": \"bassin\"\n },\n \"filter\": \"nivelle\"\n },\n {\n \"variableComponentKey\": {\n \"variable\": \"espece\",\n \"component\": \"value\"\n },\n \"filter\": \"tr\"\n }\n ]\n }\n","value":"Select by filter. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"select filter by filter numeric)":{"description":" {\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"Nombre d'individus\",\n \"component\": \"value\"\n },\n \"filter\": 42\n }\n ]\n }\n","value":"Select by numeric. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"select by rowIds":{"description":" {\n \"rowIds\" : [\n \"59ae5fec-8ed5-495b-b201-5475ea50906e\"\n ]\n }\n","value":"Find by RowIds. You can provide an optional json with rowIds","$ref":"fr.inra.oresing.model.data.DownloadDatasetQuery.class"},"select filter by filter with regexp (~ '^[ao]m+)":{"description":" {\n \"variableComponentFilters\": [\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"espece\",\n \"component\": \"value\"\n },\n \"filter\": \"^[ao]m+\",\n \"isRegExp\": \"true\"\n }\n ]\n }\n","value":"Select by RegExp. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"select filter by filter date (With declared pattern)":{"description":" {\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"date\",\n \"component\": \"time\"\n },\n \"filter\": \"12:45:00\"\n }\n ]\n }\n","value":"Select by date. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"reduce by select":{"description":" {\n \"variableComponentSelects\": [\n {\n \"variable\": \"espece\",\n \"component\": \"value\"\n }\n ]\n }\n","value":"Select by. You can provide an optional json with variableComponentSelects","$ref":"fr.inra.oresing.model.data.DownloadDatasetQuery.class"},"order by":{"description":" {\n \"variableComponentOrderBy\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"espece\",\n \"component\": \"value\"\n },\n \"order\": \"ASC\"\n }\n ]\n }\n","value":"Order by. You can provide an optional json with variableComponentOrderBy","$ref":"fr.inra.oresing.model.data.DownloadDatasetQuery.class"},"select filter by filter date by interval (With declared pattern)":{"description":" {\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"bug\",\n \"component\": \"date\"\n },\n \"type\": \"date\",\n \"format\": \"dd/MM/yyyy\",\n \"intervalValues\": {\n \"from\": \"01/01/1984\",\n \"to\": \"04/01/1984\"\n }\n }\n ]\n }\n","value":"Select by interval of date. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"},"general case":{"description":" {\n \"variableComponentSelects\": [...],\n \"variableComponentFilters\": [...],\n \"variableComponentOrderBy\": [...]\n }\n","value":"General case. You can provide an optional json with variableComponentSelects, rowIds, authorizationDescriptions, variableComponentFilters, variableComponentOrderBy","$ref":"fr.inra.oresing.model.data.DownloadDatasetQuery.class"},"select filter by filter numeric by interval)":{"description":" {\n \"variableComponentFilters\": [\n {\n \"variableComponentKey\": {\n \"variable\": \"Nombre d'individus\",\n \"component\": \"value\"\n },\n \"intervalValues\": {\n \"from\": 25,\n \"to\": 30\n }\n }\n ]\n }\n","value":"Select by interval of numeric. You can provide an optional json with variableComponentFilters","$ref":"fr.inra.oresing.model.data.authorizationDescriptions.class"}}},{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dataType","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetDataResult"}}}}}}},"/api/v1/applications/{nameOrId}/authorization/{authorizationId}":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizationById","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationResult"}}}}}},"delete":{"tags":["authorization-resources"],"operationId":"revokeAuthorization","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string","format":"uuid"}}}}}}},"/api/v1/applications/{nameOrId}/additionalfiles/authorization":{"get":{"tags":["authorization-resources"],"operationId":"getAdditionalFilesAuthorizations","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":true,"schema":{"$ref":"#/components/schemas/MultiValueMapStringString"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuthorizationAdditionalFilesResults"}}}}}}},"/api/v1/applications/{nameOrId}/additionalFiles":{"get":{"tags":["ore-si-resources"],"summary":"Returns a zip containing additional files and their description","description":"Get a additionalFiles with their description using search params","operationId":"getAdditionalFilesNamesZip","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/octet-stream":{"schema":{"$ref":"#/components/schemas/StreamingResponseBody"}}}}}},"delete":{"tags":["ore-si-resources"],"summary":"Delete additional file based on params search","description":"Delete a additionalFiles ","operationId":"removeAdditionalFiles","parameters":[{"name":"nameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"params","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/references/authorization/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getReferencesAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsReferencesResult"}}}}}}},"/api/v1/applications/{applicationNameOrId}/authorization/user/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsResult"}}}}}}},"/api/v1/applications/{applicationNameOrId}/additionalFiles/authorization/{userLoginOrId}":{"get":{"tags":["authorization-resources"],"operationId":"getAdditionalFilesAuthorizationsForUser","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"userLoginOrId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationsAdditionalFilesResult"}}}}}}},"/":{"get":{"tags":["home-resources"],"operationId":"home","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/hal+json":{"schema":{"$ref":"#/components/schemas/RedirectView"}}}}}}},"/api/v1/logout":{"delete":{"tags":["authentication-resources"],"operationId":"logout","responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/hal+json":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/references/authorization/{authorizationId}":{"delete":{"tags":["authorization-resources"],"operationId":"revokeReferencesAuthorization","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}},"/api/v1/applications/{applicationNameOrId}/additionalFiles/authorization/{authorizationId}":{"delete":{"tags":["authorization-resources"],"operationId":"revokeAdditionalFilesAuthorization","parameters":[{"name":"applicationNameOrId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorizationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Bad Request","content":{"application/hal+json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ObjectError"}}}}},"200":{"description":"OK","content":{"application/json":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"ObjectError":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"string"}},"arguments":{"type":"array","items":{"type":"object"}},"defaultMessage":{"type":"string"},"objectName":{"type":"string"},"code":{"type":"string"}}},"CreateUserRequest":{"type":"object","properties":{"login":{"type":"string"},"password":{"type":"string"},"email":{"type":"string"},"newPassword":{"type":"string"},"newPasswordConfirm":{"type":"string"},"verificationKey":{"type":"string"},"charte":{"type":"string"}}},"CreateUserResult":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"login":{"type":"string"},"email":{"type":"string"},"accountState":{"type":"string","enum":["idle","active","pending","closed"]},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"OreSiUser":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"creationDate":{"type":"string","format":"date-time"},"updateDate":{"type":"string","format":"date-time"},"login":{"type":"string"},"password":{"type":"string"},"email":{"type":"string"},"authorizations":{"type":"array","items":{"type":"string"}},"accountstate":{"type":"string","enum":["idle","active","pending","closed"]},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"ReactiveResultObject":{"type":"object"},"LoginResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"login":{"type":"string"},"email":{"type":"string"},"state":{"type":"string"},"authorizedForApplicationCreation":{"type":"boolean"},"superadmin":{"type":"boolean"},"authorizations":{"type":"array","items":{"type":"string"}},"chartes":{"type":"object","additionalProperties":{"type":"string","format":"date-time"}}}},"Authorization":{"type":"object","properties":{"timeScope":{"$ref":"#/components/schemas/LocalDateTimeRange"},"dataGroups":{"type":"array","items":{"type":"string"}},"requiredAuthorizations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Ltree"}},"intervalDates":{"type":"object","additionalProperties":{"type":"string","format":"date"},"writeOnly":true}}},"CreateAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}}}},"CreateRightsRequestRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"fields":{"type":"object","additionalProperties":{"type":"string"}},"rightsRequest":{"$ref":"#/components/schemas/CreateAuthorizationRequest"},"setted":{"type":"boolean"},"comment":{"type":"string"}}},"LocalDateTimeRange":{"type":"object","properties":{"range":{"$ref":"#/components/schemas/RangeLocalDateTime"}}},"Ltree":{"type":"object","properties":{"sql":{"type":"string"}}},"RangeLocalDateTime":{"type":"object","properties":{"empty":{"type":"boolean"}}},"CreateReferenceAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"references":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"CreateAdditionalFileAuthorizationRequest":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"usersId":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}},"applicationNameOrId":{"type":"string"},"additionalFiles":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"AddVariableMigrationDescription":{"type":"object","properties":{"defaultValue":{"type":"string"}}},"AdditionalFile":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string"}}}},"AdditionalFileDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFileFieldFormat"}}}},"AdditionalFileFieldFormat":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"}}},"ApplicationDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"name":{"type":"string"},"version":{"type":"integer","format":"int32"},"defaultLanguage":{"type":"object","properties":{"language":{"type":"string"},"displayName":{"type":"string"},"country":{"type":"string"},"variant":{"type":"string"},"script":{"type":"string"},"unicodeLocaleAttributes":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"unicodeLocaleKeys":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"displayLanguage":{"type":"string"},"displayScript":{"type":"string"},"displayCountry":{"type":"string"},"displayVariant":{"type":"string"},"extensionKeys":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"iso3Language":{"type":"string"},"iso3Country":{"type":"string"}}},"internationalization":{"$ref":"#/components/schemas/InternationalizationApplicationMap"}}},"ApplicationResult":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"title":{"type":"string"},"comment":{"type":"string"},"internationalization":{"$ref":"#/components/schemas/InternationalizationMap"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Reference"}},"authorizationReferencesRights":{"$ref":"#/components/schemas/AuthorizationsForUserResult"},"referenceSynthesis":{"type":"array","items":{"$ref":"#/components/schemas/ReferenceSynthesis"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataType"}},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFile"}},"authorizationsDatatypesRights":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"rightsRequest":{"$ref":"#/components/schemas/RightsRequest"},"configuration":{"$ref":"#/components/schemas/Configuration"},"isAdministrator":{"type":"boolean"}}},"AuthorizationColumnsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"display":{"type":"boolean"},"title":{"type":"string"},"withPeriods":{"type":"boolean"},"withDataGroups":{"type":"boolean"},"forPublic":{"type":"boolean"},"forRequest":{"type":"boolean"}}},"AuthorizationDescription":{"type":"object","properties":{"timeScope":{"$ref":"#/components/schemas/VariableComponentKey"},"authorizationScopes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AuthorizationScopeDescription"}},"dataGroups":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataGroupDescription"}},"columnsDescription":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AuthorizationColumnsDescription"}},"internationalization":{"$ref":"#/components/schemas/InternationalizationAuthorisationMap"}}},"AuthorizationScopeDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"variable":{"type":"string"},"component":{"type":"string"},"variableComponentKey":{"$ref":"#/components/schemas/VariableComponentKey"}}},"AuthorizationsForUserResult":{"type":"object","properties":{"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"boolean"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"},"userId":{"type":"string"}}},"Chart":{"type":"object","properties":{"value":{"type":"string"},"aggregation":{"$ref":"#/components/schemas/VariableComponentKey"},"unit":{"type":"string"},"gap":{"type":"string"},"standardDeviation":{"type":"string"}}},"CheckerConfigurationDescription":{"type":"object","properties":{"pattern":{"type":"string"},"refType":{"type":"string"},"groovy":{"$ref":"#/components/schemas/GroovyConfiguration"},"duration":{"type":"string"},"min":{"type":"string"},"max":{"type":"string"},"transformation":{"$ref":"#/components/schemas/TransformationConfigurationDescription"},"required":{"type":"boolean"},"multiplicity":{"type":"string","enum":["ONE","MANY"]}}},"CheckerDescription":{"type":"object","properties":{"name":{"type":"string","enum":["Reference","Boolean","Date","Integer","Float","String","GroovyExpression"]},"params":{"$ref":"#/components/schemas/CheckerConfigurationDescription"}}},"Column":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"key":{"type":"boolean"},"linkedTo":{"type":"string"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"ColumnBindingDescription":{"type":"object","properties":{"header":{"type":"string"},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]}}},"Component":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"CompositeReferenceComponentDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"reference":{"type":"string"},"parentKeyColumn":{"type":"string"},"parentRecursiveKey":{"type":"string"}}},"CompositeReferenceDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"components":{"type":"array","items":{"$ref":"#/components/schemas/CompositeReferenceComponentDescription"}}}},"ComputedVariableComponentDescription":{"type":"object","properties":{"checker":{"$ref":"#/components/schemas/CheckerDescription"},"tags":{"type":"array","items":{"type":"string"}},"computation":{"$ref":"#/components/schemas/GroovyConfiguration"},"hidden":{"type":"boolean"}}},"Configuration":{"type":"object","properties":{"tags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"rightsRequest":{"$ref":"#/components/schemas/RightsRequestDescription"},"requiredAuthorizationsAttributes":{"type":"array","items":{"type":"string"}},"version":{"type":"integer","format":"int32"},"internationalization":{"$ref":"#/components/schemas/InternationalizationMap"},"comment":{"type":"string"},"application":{"$ref":"#/components/schemas/ApplicationDescription"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceDescription"}},"compositeReferences":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/CompositeReferenceDescription"}},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AdditionalFileDescription"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DataTypeDescription"}}}},"DataGroupDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"label":{"type":"string"},"data":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"DataType":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"variables":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Variable"}},"repository":{"$ref":"#/components/schemas/Repository"},"hasAuthorizations":{"type":"boolean"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"DataTypeDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplays":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDisplay"}},"tags":{"type":"array","items":{"type":"string"}},"format":{"$ref":"#/components/schemas/FormatDescription"},"data":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/VariableDescription"}},"validations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineValidationRuleWithVariableComponentsDescription"}},"uniqueness":{"type":"array","items":{"$ref":"#/components/schemas/VariableComponentKey"}},"migrations":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/MigrationDescription"}}},"authorization":{"$ref":"#/components/schemas/AuthorizationDescription"},"repository":{"$ref":"#/components/schemas/RepositoryDescription"}}},"DynamicColumn":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"headerPrefix":{"type":"string"},"reference":{"type":"string"},"referenceColumnToLookForHeader":{"type":"string"},"presenceConstraint":{"type":"boolean"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"FieldFormat":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"}}},"FormatDescription":{"type":"object","properties":{"headerLine":{"type":"integer","format":"int32"},"firstRowLine":{"type":"integer","format":"int32"},"separator":{"type":"string"},"columns":{"type":"array","items":{"$ref":"#/components/schemas/ColumnBindingDescription"}},"repeatedColumns":{"type":"array","items":{"$ref":"#/components/schemas/RepeatedColumnBindingDescription"}},"constants":{"type":"array","items":{"$ref":"#/components/schemas/HeaderConstantDescription"}},"allowUnexpectedColumns":{"type":"boolean"}}},"GroovyConfiguration":{"type":"object","properties":{"expression":{"type":"string"},"references":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"datatypes":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"HeaderConstantDescription":{"type":"object","properties":{"rowNumber":{"type":"integer","format":"int32"},"columnNumber":{"type":"integer","format":"int32"},"headerName":{"type":"string"},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"exportHeader":{"type":"string"}}},"HeaderPatternToken":{"type":"object","properties":{"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"},"exportHeader":{"type":"string"}}},"Internationalization":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"InternationalizationAdditonalFilesMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationApplicationMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"InternationalizationAuthorisationMap":{"type":"object","properties":{"dataGroups":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}},"authorizationScopes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}},"columnsDescription":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAuthorisationName"}}}},"InternationalizationAuthorisationName":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"InternationalizationDataTypeMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"authorization":{"$ref":"#/components/schemas/InternationalizationAuthorisationMap"},"internationalizationDisplay":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDisplay"}},"internationalizedValidations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationDisplay":{"type":"object","properties":{"pattern":{"type":"object","additionalProperties":{"type":"string"}}}},"InternationalizationMap":{"type":"object","properties":{"application":{"$ref":"#/components/schemas/InternationalizationApplicationMap"},"references":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationReferenceMap"}},"dataTypes":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationDataTypeMap"}},"internationalizedTags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"rightsRequest":{"$ref":"#/components/schemas/InternationalizationRightsRequestMap"},"additionalFiles":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/InternationalizationAdditonalFilesMap"}}}},"InternationalizationReferenceMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizedDynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedValidations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizedTags":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"InternationalizationRightsRequestMap":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"description":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}}}},"LineValidationRuleWithColumnsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"columns":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"LineValidationRuleWithVariableComponentsDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"variableComponents":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/VariableComponentKey"}}}},"MigrationDescription":{"type":"object","properties":{"strategy":{"type":"string","enum":["ADD_VARIABLE"]},"dataGroup":{"type":"string"},"variable":{"type":"string"},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AddVariableMigrationDescription"}}}},"Reference":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"children":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"columns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Column"}},"dynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DynamicColumn"}},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"ReferenceDescription":{"type":"object","properties":{"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"internationalizedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Internationalization"}},"internationalizationDisplay":{"$ref":"#/components/schemas/InternationalizationDisplay"},"tags":{"type":"array","items":{"type":"string"}},"separator":{"type":"string"},"keyColumns":{"type":"array","items":{"type":"string"}},"columns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceStaticNotComputedColumnDescription"}},"computedColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceStaticComputedColumnDescription"}},"dynamicColumns":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReferenceDynamicColumnDescription"}},"validations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineValidationRuleWithColumnsDescription"}},"allowUnexpectedColumns":{"type":"boolean"}}},"ReferenceDynamicColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"headerPrefix":{"type":"string"},"reference":{"type":"string"},"referenceColumnToLookForHeader":{"type":"string"}}},"ReferenceStaticComputedColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"headerName":{"type":"string"},"computation":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"ReferenceStaticNotComputedColumnDescription":{"type":"object","properties":{"presenceConstraint":{"type":"string","enum":["MANDATORY","OPTIONAL","ABSENT"]},"tags":{"type":"array","items":{"type":"string"}},"checker":{"$ref":"#/components/schemas/CheckerDescription"},"headerName":{"type":"string"},"defaultValue":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"ReferenceSynthesis":{"type":"object","properties":{"ReferenceType":{"type":"string"},"lineCount":{"type":"integer","format":"int32"},"referenceType":{"type":"string"}}},"RepeatedColumnBindingDescription":{"type":"object","properties":{"headerPattern":{"type":"string"},"exportHeader":{"type":"string"},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/HeaderPatternToken"}},"boundTo":{"$ref":"#/components/schemas/VariableComponentKey"}}},"Repository":{"type":"object","properties":{"filePattern":{"type":"string"},"authorizationScope":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"startDate":{"$ref":"#/components/schemas/TokenDateDescription"},"endDate":{"$ref":"#/components/schemas/TokenDateDescription"}}},"RepositoryDescription":{"type":"object","properties":{"filePattern":{"type":"string"},"authorizationScope":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}},"startDate":{"$ref":"#/components/schemas/TokenDateDescription"},"endDate":{"$ref":"#/components/schemas/TokenDateDescription"}}},"RightsRequest":{"type":"object","properties":{"description":{"$ref":"#/components/schemas/RightsRequestDescription"}}},"RightsRequestDescription":{"type":"object","properties":{"description":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}},"format":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FieldFormat"}}}},"TokenDateDescription":{"type":"object","properties":{"token":{"type":"integer","format":"int32"}}},"TransformationConfigurationDescription":{"type":"object","properties":{"codify":{"type":"boolean"},"groovy":{"$ref":"#/components/schemas/GroovyConfiguration"}}},"Variable":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Component"}},"chartDescription":{"$ref":"#/components/schemas/Chart"},"tags":{"uniqueItems":true,"type":"array","items":{"type":"string"}}}},"VariableComponentKey":{"type":"object","properties":{"variable":{"type":"string"},"component":{"type":"string"},"id":{"type":"string"}}},"VariableComponentWithDefaultValueDescription":{"type":"object","properties":{"checker":{"$ref":"#/components/schemas/CheckerDescription"},"tags":{"type":"array","items":{"type":"string"}},"defaultValue":{"$ref":"#/components/schemas/GroovyConfiguration"},"hidden":{"type":"boolean"}}},"VariableDescription":{"type":"object","properties":{"chartDescription":{"$ref":"#/components/schemas/Chart"},"tags":{"type":"array","items":{"type":"string"}},"components":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/VariableComponentWithDefaultValueDescription"}},"computedComponents":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ComputedVariableComponentDescription"}},"hidden":{"type":"boolean"}}},"AuthorizationParsed":{"type":"object","properties":{"path":{"type":"string"},"dataGroups":{"type":"array","items":{"type":"string"}},"requiredAuthorizations":{"type":"object","additionalProperties":{"type":"string"}},"fromDay":{"type":"string","format":"date"},"toDay":{"type":"string","format":"date"}}},"GetRightsRequestResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"rightsRequests":{"type":"array","items":{"$ref":"#/components/schemas/RightsRequestResult"}},"description":{"$ref":"#/components/schemas/RightsRequestDescription"}}},"RightsRequestResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"application":{"type":"string","format":"uuid"},"user":{"type":"string","format":"uuid"},"comment":{"type":"string"},"rightsRequestForm":{"type":"object","additionalProperties":{"type":"string"}},"rightsRequest":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"authorizationByDatatypeAndPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"setted":{"type":"boolean"}}},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"}}},"MultiValueMapStringString":{"type":"object","properties":{"all":{"type":"object","additionalProperties":{"type":"string"},"writeOnly":true},"empty":{"type":"boolean"}},"additionalProperties":{"type":"array","items":{"type":"string"}}},"GetReferenceResult":{"type":"object","properties":{"referenceValues":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/ReferenceValue"}},"referenceTypeForReferencingColumns":{"type":"object","additionalProperties":{"type":"string"}}}},"ReferenceValue":{"type":"object","properties":{"id":{"type":"string"},"hierarchicalKey":{"type":"string"},"hierarchicalReference":{"type":"string"},"naturalKey":{"type":"string"},"values":{"type":"object"},"refsLinkedTo":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}}},"referencingReference":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}},"StreamingResponseBody":{"type":"object"},"AuthorizationsReferencesResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"}}},"GetAuthorizationReferencesResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"GetAuthorizationReferencesResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationReferencesResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsReferencesResult"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}}}},"AuthorizationScope":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"options":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/Option"}}}},"AuthorizationsResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"applicationName":{"type":"string"},"authorizationByPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"isAdministrator":{"type":"boolean"}}},"ColumnDescription":{"type":"object","properties":{"display":{"type":"boolean"},"title":{"type":"string"},"withPeriods":{"type":"boolean"},"withDataGroups":{"type":"boolean"},"forPublic":{"type":"boolean"},"forRequest":{"type":"boolean"},"internationalizationName":{"type":"object","properties":{"empty":{"type":"boolean"}},"additionalProperties":{"type":"string"}}}},"DataGroup":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"GetGrantableResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"dataGroups":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/DataGroup"}}},"authorizationScopes":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/AuthorizationScope"}}},"columnsDescription":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ColumnDescription"}}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"},"publicAuthorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}}}},"Option":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}}},"BinaryFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"creationDate":{"type":"string","format":"date-time"},"updateDate":{"type":"string","format":"date-time"},"application":{"type":"string","format":"uuid"},"name":{"type":"string"},"comment":{"type":"string"},"size":{"type":"integer","format":"int64"},"data":{"type":"array","items":{"type":"string","format":"byte"}},"params":{"$ref":"#/components/schemas/BinaryFileInfos"}}},"BinaryFileDataset":{"type":"object","properties":{"datatype":{"type":"string"},"requiredAuthorizations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Ltree"}},"from":{"type":"string"},"to":{"type":"string"},"comment":{"type":"string"}}},"BinaryFileInfos":{"type":"object","properties":{"binaryFiledataset":{"$ref":"#/components/schemas/BinaryFileDataset"},"published":{"type":"boolean"},"publisheduser":{"type":"string","format":"uuid"},"publisheddate":{"type":"string"},"createuser":{"type":"string","format":"uuid"},"createdate":{"type":"string"},"comment":{"type":"string"}}},"CheckerTarget":{"type":"object"},"DataRowResult":{"type":"object","properties":{"rowId":{"type":"string"},"values":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object"}}},"refsLinkedTo":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"uniqueItems":true,"type":"array","items":{"type":"string","format":"uuid"}}}},"totalRows":{"type":"integer","format":"int64"},"rowNumber":{"type":"integer","format":"int64"}}},"FieldType":{"type":"object","properties":{"value":{"type":"object"},"sqlType":{"type":"string","enum":["UUID","LTREE","TEXT","INTEGER","NUMERIC","COMPOSITE_DATE","BOOLEAN","JSONB"]}}},"GetDataResult":{"type":"object","properties":{"variables":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"rows":{"type":"array","items":{"$ref":"#/components/schemas/DataRowResult"}},"totalRows":{"type":"integer","format":"int64"},"checkedFormatVariableComponents":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/LineCheckerWarper"}}}}},"LineCheckerWarper":{"type":"object","properties":{"checkerDescription":{"$ref":"#/components/schemas/CheckerDescription"},"target":{"$ref":"#/components/schemas/CheckerTarget"},"fieldType":{"$ref":"#/components/schemas/FieldType"},"transformer":{"$ref":"#/components/schemas/LineTransformer"},"sqlType":{"type":"string","enum":["UUID","LTREE","TEXT","INTEGER","NUMERIC","COMPOSITE_DATE","BOOLEAN","JSONB"]},"multiplicity":{"type":"string","enum":["ONE","MANY"]},"underlyingType":{"$ref":"#/components/schemas/FieldType"}}},"LineTransformer":{"type":"object"},"GetAuthorizationResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"publicAuthorizations":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/Authorization"}}}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"}}},"GetAuthorizationResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsResult"}}},"AuthorizationsAdditionalFilesResult":{"type":"object","properties":{"authorizationResults":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}},"applicationName":{"type":"string"},"isAdministrator":{"type":"boolean"}}},"GetAuthorizationAdditionalFilesResult":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/OreSiUser"}},"application":{"type":"string","format":"uuid"},"authorizations":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"GetAuthorizationAdditionalFilesResults":{"type":"object","properties":{"authorizationResults":{"uniqueItems":true,"type":"array","properties":{"empty":{"type":"boolean"}},"items":{"$ref":"#/components/schemas/GetAuthorizationAdditionalFilesResult"}},"authorizationsForUser":{"$ref":"#/components/schemas/AuthorizationsAdditionalFilesResult"},"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}}}},"AdditionalBinaryFileResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"application":{"type":"string","format":"uuid"},"user":{"type":"string","format":"uuid"},"updateUser":{"type":"string","format":"uuid"},"additionalBinaryFileType":{"type":"string"},"comment":{"type":"string"},"fileName":{"type":"string"},"fileType":{"type":"string"},"size":{"type":"integer","format":"int64"},"additionalBinaryFileForm":{"type":"object","additionalProperties":{"type":"string"}},"associates":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}},"associatesByDatatypeAndPath":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/AuthorizationParsed"}}}}},"updateDate":{"type":"string","format":"date-time"},"forApplication":{"type":"boolean"}}},"GetAdditionalFilesResult":{"type":"object","properties":{"users":{"uniqueItems":true,"type":"array","items":{"$ref":"#/components/schemas/User"}},"additionalFileName":{"type":"string"},"additionalBinaryFiles":{"type":"array","items":{"$ref":"#/components/schemas/AdditionalBinaryFileResult"}},"description":{"$ref":"#/components/schemas/AdditionalFileDescription"},"fileNames":{"type":"array","items":{"type":"string"}}}},"ApplicationContext":{"type":"object","properties":{"parent":{"$ref":"#/components/schemas/ApplicationContext"},"id":{"type":"string"},"displayName":{"type":"string"},"applicationName":{"type":"string"},"autowireCapableBeanFactory":{"$ref":"#/components/schemas/AutowireCapableBeanFactory"},"startupDate":{"type":"integer","format":"int64"},"environment":{"$ref":"#/components/schemas/Environment"},"beanDefinitionCount":{"type":"integer","format":"int32"},"beanDefinitionNames":{"type":"array","items":{"type":"string"}},"parentBeanFactory":{"$ref":"#/components/schemas/BeanFactory"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"parent":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"unnamedModule":{"type":"object","properties":{"name":{"type":"string"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"descriptor":{"type":"object","properties":{"open":{"type":"boolean"},"automatic":{"type":"boolean"}}},"named":{"type":"boolean"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"packages":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"nativeAccessEnabled":{"type":"boolean"},"layer":{"type":"object"}}},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"unnamedModule":{"type":"object","properties":{"name":{"type":"string"},"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"descriptor":{"type":"object","properties":{"open":{"type":"boolean"},"automatic":{"type":"boolean"}}},"named":{"type":"boolean"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"packages":{"uniqueItems":true,"type":"array","items":{"type":"string"}},"nativeAccessEnabled":{"type":"boolean"},"layer":{"type":"object"}}},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}}}},"AutowireCapableBeanFactory":{"type":"object"},"BeanFactory":{"type":"object"},"Environment":{"type":"object","properties":{"activeProfiles":{"type":"array","items":{"type":"string"}},"defaultProfiles":{"type":"array","items":{"type":"string"}}}},"FilterRegistration":{"type":"object","properties":{"servletNameMappings":{"type":"array","items":{"type":"string"}},"urlPatternMappings":{"type":"array","items":{"type":"string"}},"initParameters":{"type":"object","additionalProperties":{"type":"string"}},"name":{"type":"string"},"className":{"type":"string"}}},"HttpStatusCode":{"type":"object","properties":{"error":{"type":"boolean"},"is4xxClientError":{"type":"boolean"},"is2xxSuccessful":{"type":"boolean"},"is5xxServerError":{"type":"boolean"},"is1xxInformational":{"type":"boolean"},"is3xxRedirection":{"type":"boolean"}}},"JspConfigDescriptor":{"type":"object","properties":{"jspPropertyGroups":{"type":"array","items":{"$ref":"#/components/schemas/JspPropertyGroupDescriptor"}},"taglibs":{"type":"array","items":{"$ref":"#/components/schemas/TaglibDescriptor"}}}},"JspPropertyGroupDescriptor":{"type":"object","properties":{"defaultContentType":{"type":"string"},"buffer":{"type":"string"},"urlPatterns":{"type":"array","items":{"type":"string"}},"errorOnELNotFound":{"type":"string"},"pageEncoding":{"type":"string"},"scriptingInvalid":{"type":"string"},"includePreludes":{"type":"array","items":{"type":"string"}},"includeCodas":{"type":"array","items":{"type":"string"}},"trimDirectiveWhitespaces":{"type":"string"},"errorOnUndeclaredNamespace":{"type":"string"},"deferredSyntaxAllowedAsLiteral":{"type":"string"},"isXml":{"type":"string"},"elIgnored":{"type":"string"}}},"RedirectView":{"type":"object","properties":{"applicationContext":{"$ref":"#/components/schemas/ApplicationContext"},"servletContext":{"$ref":"#/components/schemas/ServletContext"},"contentType":{"type":"string"},"requestContextAttribute":{"type":"string"},"staticAttributes":{"type":"object","additionalProperties":{"type":"object"}},"exposePathVariables":{"type":"boolean"},"exposeContextBeansAsAttributes":{"type":"boolean","writeOnly":true},"exposedContextBeanNames":{"type":"array","writeOnly":true,"items":{"type":"string"}},"beanName":{"type":"string"},"url":{"type":"string"},"contextRelative":{"type":"boolean","writeOnly":true},"http10Compatible":{"type":"boolean","writeOnly":true},"exposeModelAttributes":{"type":"boolean","writeOnly":true},"encodingScheme":{"type":"string","writeOnly":true},"statusCode":{"$ref":"#/components/schemas/HttpStatusCode"},"expandUriTemplateVariables":{"type":"boolean","writeOnly":true},"propagateQueryParams":{"type":"boolean","writeOnly":true},"hosts":{"type":"array","items":{"type":"string"}},"propagateQueryProperties":{"type":"boolean"},"redirectView":{"type":"boolean"},"attributes":{"type":"object","additionalProperties":{"type":"string"},"writeOnly":true},"attributesCSV":{"type":"string","writeOnly":true},"attributesMap":{"type":"object","additionalProperties":{"type":"object"}}}},"ServletContext":{"type":"object","properties":{"classLoader":{"type":"object","properties":{"name":{"type":"string"},"registeredAsParallelCapable":{"type":"boolean"},"definedPackages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"annotations":{"type":"array","items":{"type":"object"}},"declaredAnnotations":{"type":"array","items":{"type":"object"}},"sealed":{"type":"boolean"},"specificationTitle":{"type":"string"},"specificationVersion":{"type":"string"},"specificationVendor":{"type":"string"},"implementationTitle":{"type":"string"},"implementationVersion":{"type":"string"},"implementationVendor":{"type":"string"}}}},"defaultAssertionStatus":{"type":"boolean","writeOnly":true}}},"majorVersion":{"type":"integer","format":"int32"},"minorVersion":{"type":"integer","format":"int32"},"attributeNames":{"type":"object"},"effectiveMajorVersion":{"type":"integer","format":"int32"},"effectiveMinorVersion":{"type":"integer","format":"int32"},"serverInfo":{"type":"string"},"servletContextName":{"type":"string"},"defaultSessionTrackingModes":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"servletRegistrations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ServletRegistration"}},"filterRegistrations":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FilterRegistration"}},"sessionCookieConfig":{"$ref":"#/components/schemas/SessionCookieConfig"},"sessionTrackingModes":{"uniqueItems":true,"type":"array","writeOnly":true,"items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"effectiveSessionTrackingModes":{"uniqueItems":true,"type":"array","items":{"type":"string","enum":["COOKIE","URL","SSL"]}},"jspConfigDescriptor":{"$ref":"#/components/schemas/JspConfigDescriptor"},"virtualServerName":{"type":"string"},"sessionTimeout":{"type":"integer","format":"int32"},"requestCharacterEncoding":{"type":"string"},"responseCharacterEncoding":{"type":"string"},"contextPath":{"type":"string"},"initParameterNames":{"type":"object"}}},"ServletRegistration":{"type":"object","properties":{"runAsRole":{"type":"string"},"mappings":{"type":"array","items":{"type":"string"}},"initParameters":{"type":"object","additionalProperties":{"type":"string"}},"name":{"type":"string"},"className":{"type":"string"}}},"SessionCookieConfig":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"attributes":{"type":"object","additionalProperties":{"type":"string"}},"comment":{"type":"string","deprecated":true},"domain":{"type":"string"},"maxAge":{"type":"integer","format":"int32"},"secure":{"type":"boolean"},"httpOnly":{"type":"boolean"}}},"TaglibDescriptor":{"type":"object","properties":{"taglibURI":{"type":"string"},"taglibLocation":{"type":"string"}}}}}} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8cbd9cbbf9cc6f130dab0e7d823e8f145c242d81..5c43fdfbbf3dc0475c0ec9f0f6bcd7732618d950 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> - <version>3.1.2</version> + <version>3.1.4</version> </parent> @@ -65,15 +65,21 @@ <license.licenseName>lgpl_v3</license.licenseName> <!-- Java Version --> - <java.version>17</java.version> + <java.version>21</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <jwt.version>0.10.5</jwt.version> - <flyway-spring-test.version>5.2.1</flyway-spring-test.version> - <springfox-swagger2.version>3.0.0</springfox-swagger2.version> - - + <flyway-spring-test.version>9.5.0</flyway-spring-test.version> + <springdoc-openapi-starter-webmvc-ui.version>2.1.0</springdoc-openapi-starter-webmvc-ui.version> + <common-csv.version>1.10.0</common-csv.version> + <commons-io.version>2.13.0</commons-io.version> + <guava.version>32.1.1-jre</guava.version> + <opencsv.version>5.8</opencsv.version> + <bcrypt.version>0.10.2</bcrypt.version> + <vue.version>2.6.14</vue.version> + <testcontainer.postgresql.version>1.18.3</testcontainer.postgresql.version> + <groovy.version-jsr223>3.0.19</groovy.version-jsr223> <!--Database infos --> @@ -128,7 +134,12 @@ <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> - <version>2.1.0</version> + <version>${springdoc-openapi-starter-webmvc-ui.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <optional>true</optional> </dependency> <!-- ******************************************************* --> @@ -145,7 +156,7 @@ <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> - <version>1.8</version> + <version>${common-csv.version}</version> </dependency> <dependency> @@ -156,13 +167,13 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.8.0</version> + <version>${commons-io.version}</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> - <version>30.0-jre</version> + <version>${guava.version}</version> </dependency> <dependency> @@ -186,7 +197,7 @@ <dependency> <groupId>at.favre.lib</groupId> <artifactId>bcrypt</artifactId> - <version>0.9.0</version> + <version>${bcrypt.version}</version> </dependency> <dependency> @@ -200,50 +211,37 @@ <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> - <version>5.8</version> + <version>${opencsv.version}</version> </dependency> - - <!-- <dependency> - <groupId>io.springfox</groupId> - <artifactId>springfox-swagger2</artifactId> - <version>${springfox-swagger2.version}</version> - </dependency> - - <dependency> - <groupId>io.springfox</groupId> - <artifactId>springfox-swagger-ui</artifactId> - <version>${springfox-swagger2.version}</version> - </dependency>--> - <!-- UI --> <dependency> <groupId>org.webjars.bowergithub.vuejs</groupId> <artifactId>vue</artifactId> - <version>2.6.2</version> - </dependency> - - <dependency> - <groupId>org.testcontainers</groupId> - <artifactId>postgresql</artifactId> - <version>1.15.3</version> + <version>${vue.version}</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-jsr223</artifactId> - <version>3.0.16</version> + <version>${groovy.version-jsr223}</version> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> - <version>2.21.0</version> + <version>5.4.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> + <dependency> + <groupId>org.testcontainers</groupId> + <artifactId>postgresql</artifactId> + <version>${testcontainer.postgresql.version}</version> + </dependency> + </dependencies> @@ -420,7 +418,7 @@ <reuseForks>false</reuseForks> <testSourceDirectory>src/test</testSourceDirectory> <testFailureIgnore>false</testFailureIgnore> - <groups>fr.inra.oresing.rest.HAUTE_FREQUENCE_TEST</groups> + <groups>HAUTE_FREQUENCE_TEST</groups> </configuration> </plugin> </plugins> @@ -438,7 +436,7 @@ <reuseForks>false</reuseForks> <testSourceDirectory>src/test</testSourceDirectory> <testFailureIgnore>false</testFailureIgnore> - <groups>fr.inra.oresing.rest.ACBB_TEST</groups> + <groups>ACBB_TEST</groups> </configuration> </plugin> </plugins> @@ -475,7 +473,7 @@ <reuseForks>false</reuseForks> <testSourceDirectory>src/test</testSourceDirectory> <testFailureIgnore>false</testFailureIgnore> - <groups>fr.inra.oresing.rest.SWAGGER_BUILD</groups> + <groups>SWAGGER_BUILD</groups> </configuration> </plugin> </plugins> @@ -493,7 +491,25 @@ <reuseForks>false</reuseForks> <testSourceDirectory>src/test</testSourceDirectory> <testFailureIgnore>false</testFailureIgnore> - <groups>fr.inra.oresing.rest.OTHERS_TEST</groups> + <groups>OTHERS_TEST</groups> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>test_rest_model_request_test</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <forkCount>4</forkCount> + <reuseForks>false</reuseForks> + <testSourceDirectory>src/test</testSourceDirectory> + <testFailureIgnore>false</testFailureIgnore> + <groups>MODEL_REQUEST_TEST</groups> </configuration> </plugin> </plugins> diff --git a/src/main/java/fr/inra/oresing/checker/CheckerType.java b/src/main/java/fr/inra/oresing/checker/CheckerType.java index 28e34c3bddf22debde294c8a94eb7071393e13d9..0db6f3b10d9c818fe1483178bd954870a907d64d 100644 --- a/src/main/java/fr/inra/oresing/checker/CheckerType.java +++ b/src/main/java/fr/inra/oresing/checker/CheckerType.java @@ -4,13 +4,21 @@ import com.google.common.collect.ImmutableMap; import fr.inra.oresing.checker.type.*; import fr.inra.oresing.model.Application; import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.model.data.DownloadDatasetQueryAdvancedSearch; import fr.inra.oresing.persistence.DataRepository; import fr.inra.oresing.persistence.Ltree; import fr.inra.oresing.persistence.OreSiRepository; import fr.inra.oresing.persistence.ReferenceValueRepository; import fr.inra.oresing.rest.exceptions.SiOreIllegalArgumentException; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; import fr.inra.oresing.transformer.LineTransformer; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; import java.util.*; import java.util.stream.Collectors; @@ -23,6 +31,34 @@ public enum CheckerType { String("String"), GroovyExpression("GroovyExpression"); + private final String name; + + CheckerType(String name) { + this.name = name; + } + + public static TypeOfDate getFieldType(String patternDate) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(patternDate); + String NOW = formatter.format(LocalDateTime.now()); + try { + LocalDateTime.parse(NOW, formatter); + return TypeOfDate.DATETIME; + } catch (DateTimeParseException dte) { + try { + LocalDate.parse(NOW, formatter); + return TypeOfDate.DATE; + } catch (DateTimeParseException de) { + try { + LocalTime.parse(NOW, formatter); + return TypeOfDate.TIME; + } catch (DateTimeParseException te) { + return null; + } + } + } + } + + public static SiOreIllegalArgumentException getError(CheckerType checkerType) { return new SiOreIllegalArgumentException( "badCheckerType", @@ -47,12 +83,6 @@ public enum CheckerType { return name; } - private final String name; - - CheckerType(String name) { - this.name = name; - } - @Override public String toString() { return name; @@ -106,4 +136,92 @@ public enum CheckerType { default -> new StringType(checkerDescription.getParams().getPattern()); }; } + + public static enum TypeOfDate { + DATE(LocalDate.class), + TIME(LocalDateTime.class), + DATETIME(LocalDateTime.class); + + TypeOfDate(Class<? extends TemporalAccessor> localDateClass) { + } + } + + public static sealed interface VariableComponentType permits + VariableComponentType.VariableComponentTextType, + VariableComponentType.VariableComponentReferenceType, + VariableComponentType.VariableComponentNumericType, + VariableComponentType.VariableComponentBooleanType, + VariableComponentType.VariableComponentDateType { + + public static VariableComponentType getVariableComponentType(Configuration.VariableComponentDescription variableComponentDescription) { + Configuration.CheckerDescription checkerDescription = Optional.ofNullable(variableComponentDescription) + .map(Configuration.VariableComponentDescription::getChecker) + .orElse(null); + if (checkerDescription == null) { + return new VariableComponentTextType(); + } + return switch (checkerDescription.getName()) { + case Reference -> new VariableComponentReferenceType(); + case Integer, Float -> new VariableComponentNumericType(); + case Boolean -> new VariableComponentBooleanType(); + case Date -> { + java.lang.String patternDate = checkerDescription.getParams().getPattern(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(patternDate); + DateTimeFormatter isoLocalDateTimeFormater = DateTimeFormatter.ISO_LOCAL_DATE_TIME; + String NOW = formatter.format(LocalDateTime.now()); + java.lang.String format = Optional.ofNullable(checkerDescription) + .map(Configuration.CheckerDescription::getParams) + .map(Configuration.CheckerConfigurationDescription::getPattern) + .orElseThrow(() -> new BadDownloadDatasetQuery(BadDownloadDatasetQuery.MISSING_FORMAT_FOR_FILTER)); + try { + LocalDateTime.parse(NOW, formatter); + yield new VariableComponentDateType( + format, + DownloadDatasetQueryAdvancedSearch.FieldType.datetime + ); + } catch (DateTimeParseException dte) { + try { + LocalDate.parse(NOW, formatter); + yield new VariableComponentDateType( + format, + DownloadDatasetQueryAdvancedSearch.FieldType.date + ); + } catch (DateTimeParseException de) { + try { + LocalTime.parse(NOW, formatter); + yield new VariableComponentDateType( + format, + DownloadDatasetQueryAdvancedSearch.FieldType.time + ); + } catch (DateTimeParseException te) { + yield null; + } + } + } + } + case null, default -> { + yield new VariableComponentTextType(); + } + }; + } + + record VariableComponentTextType() implements VariableComponentType { + } + + record VariableComponentReferenceType() implements VariableComponentType { + } + + record VariableComponentBooleanType() implements VariableComponentType { + } + + record VariableComponentNumericType() implements VariableComponentType { + } + + record VariableComponentDateType(String format, + DownloadDatasetQueryAdvancedSearch.FieldType fieldType) implements VariableComponentType { + } + } + + public record FormatForFieldTypeDate(String format, DownloadDatasetQueryAdvancedSearch.FieldType fieldType) { + } } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/checker/LineCheckerWarper.java b/src/main/java/fr/inra/oresing/checker/LineCheckerWarper.java index 1b965333e73160fdb25e08f023e9a3d4f65741ef..f8b73e3c1c5c6cdd8937d740efb95d5379373ef4 100644 --- a/src/main/java/fr/inra/oresing/checker/LineCheckerWarper.java +++ b/src/main/java/fr/inra/oresing/checker/LineCheckerWarper.java @@ -14,7 +14,7 @@ import lombok.Getter; import java.util.Optional; -public abstract class LineCheckerWarper<FT extends FieldType> { +public sealed abstract class LineCheckerWarper<FT extends FieldType> permits LineCheckerWarperResult, ManyCheckerWarper, OneCheckerWarper, ReferenceLineCheckerDisplay { protected Configuration.CheckerDescription checkerDescription; protected CheckerTarget target; protected FT fieldType; diff --git a/src/main/java/fr/inra/oresing/checker/LineCheckerWarperResult.java b/src/main/java/fr/inra/oresing/checker/LineCheckerWarperResult.java index b1c17bdbba05bde4abff1175502242a842e25bec..bc56be317f54580b6c3b16c96a59b40b615bfdc1 100644 --- a/src/main/java/fr/inra/oresing/checker/LineCheckerWarperResult.java +++ b/src/main/java/fr/inra/oresing/checker/LineCheckerWarperResult.java @@ -5,7 +5,7 @@ import fr.inra.oresing.model.Configuration; import fr.inra.oresing.model.Datum; import fr.inra.oresing.rest.validationcheckresults.ValidationCheckResult; import lombok.Value; -import org.testcontainers.shaded.org.apache.commons.lang3.NotImplementedException; +import org.apache.commons.lang3.NotImplementedException; @Value public class LineCheckerWarperResult extends LineCheckerWarper { diff --git a/src/main/java/fr/inra/oresing/checker/ManyCheckerWarper.java b/src/main/java/fr/inra/oresing/checker/ManyCheckerWarper.java index d86b6d7e64e10a2d1fed16ccbb77c4221199c13c..17276a173dda14f6e2c4a8c598dd47c29fe2c3fe 100644 --- a/src/main/java/fr/inra/oresing/checker/ManyCheckerWarper.java +++ b/src/main/java/fr/inra/oresing/checker/ManyCheckerWarper.java @@ -18,7 +18,7 @@ import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; -public class ManyCheckerWarper<FT extends FieldType, U extends ListType<FT>> extends LineCheckerWarper<U> { +public non-sealed class ManyCheckerWarper<FT extends FieldType, U extends ListType<FT>> extends LineCheckerWarper<U> { private U value; private FT fieldTypeForOne; diff --git a/src/main/java/fr/inra/oresing/checker/OneCheckerWarper.java b/src/main/java/fr/inra/oresing/checker/OneCheckerWarper.java index a2b413f5983b84a81176a22c93c10e4f9d2e5622..bcc6ad44049a22b541749375a336642a24ddbeb0 100644 --- a/src/main/java/fr/inra/oresing/checker/OneCheckerWarper.java +++ b/src/main/java/fr/inra/oresing/checker/OneCheckerWarper.java @@ -12,7 +12,7 @@ import com.google.common.base.Strings; import java.util.Optional; -public class OneCheckerWarper<FT extends FieldType> extends LineCheckerWarper<FT> { +public non-sealed class OneCheckerWarper<FT extends FieldType> extends LineCheckerWarper<FT> { private Supplier<OneCheckerWarper> clone; public OneCheckerWarper(Application application, Configuration.CheckerDescription checkerDescription, CheckerTarget target, LineTransformer transformer, CheckerFactory checkerFactory) { diff --git a/src/main/java/fr/inra/oresing/checker/type/AbstractType.java b/src/main/java/fr/inra/oresing/checker/type/AbstractType.java index 5d97e7980ff588f72f86a1c4b19bf7c1bf03fcfd..d08d7d44400275761fc1ff73f2407becc196f4ae 100644 --- a/src/main/java/fr/inra/oresing/checker/type/AbstractType.java +++ b/src/main/java/fr/inra/oresing/checker/type/AbstractType.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; -public abstract class AbstractType<T> implements FieldType<T>{ +public sealed abstract class AbstractType<T> implements FieldType<T> permits ReferenceType { public static FieldType readObject(Object fieldType){ if(fieldType==null){ return new NullType(); diff --git a/src/main/java/fr/inra/oresing/checker/type/BooleanType.java b/src/main/java/fr/inra/oresing/checker/type/BooleanType.java index 72cbeb59f08073203eef37f036e2660c55780e9c..97a67f42fa4fcb540030e8a91d51c28ff52a8ae8 100644 --- a/src/main/java/fr/inra/oresing/checker/type/BooleanType.java +++ b/src/main/java/fr/inra/oresing/checker/type/BooleanType.java @@ -20,7 +20,7 @@ import fr.inra.oresing.rest.validationcheckresults.ValidationCheckResult; import java.io.IOException; import java.util.*; -public class BooleanType implements FieldType<Boolean> { +public non-sealed class BooleanType implements FieldType<Boolean> { private final GroovyExpression expression; private final ImmutableMap<String, Object> context; private final Configuration.CheckerDescription checkerDescription; @@ -98,6 +98,10 @@ public class BooleanType implements FieldType<Boolean> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + return; + } gen.writeBoolean(value); } diff --git a/src/main/java/fr/inra/oresing/checker/type/DateType.java b/src/main/java/fr/inra/oresing/checker/type/DateType.java index aa29a1d1d5a42e5138909faa8f418b185c032a45..6c4ac0e525f209b2237a6e21e9b5573d4a32f232 100644 --- a/src/main/java/fr/inra/oresing/checker/type/DateType.java +++ b/src/main/java/fr/inra/oresing/checker/type/DateType.java @@ -32,7 +32,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -public class DateType implements FieldType<LocalDateTime> { +public non-sealed class DateType implements FieldType<LocalDateTime> { public static final String PATTERN_DATE_REGEXP = "^date:.{19}:"; public static final String PATTERN_DATE_REGEXP_FIND_DATE = "^date:(.{19}):(.*)"; @@ -117,7 +117,7 @@ public class DateType implements FieldType<LocalDateTime> { if(Pattern.compile(PATTERN_DATE_REGEXP_FIND_DATE).matcher(dateString).matches()){ return dateString; } - LocalDateTime date = valueToDate(dateString); + LocalDateTime date = valueToDate(formatter, dateString); return String.format("date:%s:%s", date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), pattern); } @@ -160,7 +160,7 @@ public class DateType implements FieldType<LocalDateTime> { valuetoDate = LocalDateTime.parse(group, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } try { - this.value = valuetoDate!=null?valuetoDate:valueToDate(value); + this.value = valuetoDate!=null?valuetoDate:valueToDate(formatter, value); this.sortableDate = toComparableDate(value); if (minDate != null && value.compareTo(minDate) < 0) { throw new IllegalArgumentException(LOWER_THAN_MIN); @@ -189,9 +189,9 @@ public class DateType implements FieldType<LocalDateTime> { return validationCheckResult; } - private LocalDateTime valueToDate(String value) { + public static LocalDateTime valueToDate(DateTimeFormatter formatter, String value) { value = sortableDateToFormattedDate(value.trim()); - TemporalAccessor dateParsed = this.formatter.parse(value); + TemporalAccessor dateParsed = formatter.parse(value); LocalDate localdate = dateParsed.query(TemporalQueries.localDate()); localdate = localdate == null ? LocalDate.of(1970, 1, 1) : localdate; LocalTime localTime = dateParsed.query(TemporalQueries.localTime()); @@ -213,6 +213,9 @@ public class DateType implements FieldType<LocalDateTime> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + } gen.writeString(toComparableDate(value)); } diff --git a/src/main/java/fr/inra/oresing/checker/type/FieldType.java b/src/main/java/fr/inra/oresing/checker/type/FieldType.java index 9c9b6608de052d7449dbd611fa7d7c03759c0da8..871756a7ec99bfe85b71e350004111d7f2e51761 100644 --- a/src/main/java/fr/inra/oresing/checker/type/FieldType.java +++ b/src/main/java/fr/inra/oresing/checker/type/FieldType.java @@ -18,7 +18,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -public interface FieldType<T> extends SomethingToBeStoredAsJsonInDatabase , SomethingToBeSentToFrontend{ +public sealed interface FieldType<T> extends SomethingToBeStoredAsJsonInDatabase , SomethingToBeSentToFrontend permits AbstractType, BooleanType, DateType, FloatType, IntegerType, ListType, MapType, NullType, StringType { T getValue(); SqlPrimitiveType getSqlType(); diff --git a/src/main/java/fr/inra/oresing/checker/type/FloatType.java b/src/main/java/fr/inra/oresing/checker/type/FloatType.java index f9e4abad6ed35ef3031b2bc0cc06d65394c1aaa1..f170d5eab29da98cc5a5f50e286277deca071812 100644 --- a/src/main/java/fr/inra/oresing/checker/type/FloatType.java +++ b/src/main/java/fr/inra/oresing/checker/type/FloatType.java @@ -18,7 +18,7 @@ import org.apache.logging.log4j.util.Supplier; import java.io.IOException; import java.util.*; -public class FloatType implements FieldType<Float> { +public non-sealed class FloatType implements FieldType<Float> { public static final String LOWER_THAN_MIN = "LOWER_THAN_MIN"; public static final String HIGHER_THAN_MAX = "HIGHER_THAN_MAX"; private final Float min; @@ -99,6 +99,10 @@ public class FloatType implements FieldType<Float> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + return; + } gen.writeNumber(value); } diff --git a/src/main/java/fr/inra/oresing/checker/type/IntegerType.java b/src/main/java/fr/inra/oresing/checker/type/IntegerType.java index 6a1453b22248a03256a99d1bd83a8b3b523be983..7ec57770d507528bb6a89548fcd50b24550d6b4f 100644 --- a/src/main/java/fr/inra/oresing/checker/type/IntegerType.java +++ b/src/main/java/fr/inra/oresing/checker/type/IntegerType.java @@ -18,7 +18,7 @@ import org.apache.logging.log4j.util.Supplier; import java.io.IOException; import java.util.*; -public class IntegerType implements FieldType<Integer> { +public non-sealed class IntegerType implements FieldType<Integer> { public static final String LOWER_THAN_MIN = "LOWER_THAN_MIN"; public static final String HIGHER_THAN_MAX = "HIGHER_THAN_MAX"; private final Integer min; @@ -100,6 +100,10 @@ public class IntegerType implements FieldType<Integer> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + return; + } gen.writeNumber(value); } diff --git a/src/main/java/fr/inra/oresing/checker/type/ListType.java b/src/main/java/fr/inra/oresing/checker/type/ListType.java index 84092f7250e0106a21c77ea2857e1abc525a5b67..edc0ddd5112386bc6c000d13f50ec10320a44d14 100644 --- a/src/main/java/fr/inra/oresing/checker/type/ListType.java +++ b/src/main/java/fr/inra/oresing/checker/type/ListType.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; -public class ListType<FT extends FieldType> implements FieldType<List> { +public non-sealed class ListType<FT extends FieldType> implements FieldType<List> { private final FT fieldType; List<FT> value = new LinkedList<>(); Supplier<ListType> clone; diff --git a/src/main/java/fr/inra/oresing/checker/type/MapType.java b/src/main/java/fr/inra/oresing/checker/type/MapType.java index 372ad0c08a2919d52152e7e853f1131a264e3961..ae6e27399590fd3e6149733d78fe598e7ac824e9 100644 --- a/src/main/java/fr/inra/oresing/checker/type/MapType.java +++ b/src/main/java/fr/inra/oresing/checker/type/MapType.java @@ -18,7 +18,7 @@ import org.apache.logging.log4j.util.Supplier; import java.io.IOException; import java.util.*; -public class MapType<K, V> implements FieldType<Map<K, V>> { +public non-sealed class MapType<K, V> implements FieldType<Map<K, V>> { Map<K,V> value = new HashMap<>(); Supplier<MapType> clone; diff --git a/src/main/java/fr/inra/oresing/checker/type/NullType.java b/src/main/java/fr/inra/oresing/checker/type/NullType.java index 9a4f72cb30ee09281577b9c8a8c1824fbb7f20af..52ea4aa8ea1e23a9dc5ae77da6df007e165fddc8 100644 --- a/src/main/java/fr/inra/oresing/checker/type/NullType.java +++ b/src/main/java/fr/inra/oresing/checker/type/NullType.java @@ -20,7 +20,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -public class NullType implements FieldType<NullType.Null> { +public non-sealed class NullType implements FieldType<NullType.Null> { public static NullType INSTANCE = new NullType(); Supplier<NullType> clone; diff --git a/src/main/java/fr/inra/oresing/checker/type/ReferenceType.java b/src/main/java/fr/inra/oresing/checker/type/ReferenceType.java index 2848c3d9240da68e35b0fe0aca5d5d5a6c5d918a..1446903b145aad0bb4c907e55cb1112930b9cecc 100644 --- a/src/main/java/fr/inra/oresing/checker/type/ReferenceType.java +++ b/src/main/java/fr/inra/oresing/checker/type/ReferenceType.java @@ -24,7 +24,7 @@ import java.io.IOException; import java.util.*; import java.util.stream.Collectors; -public class ReferenceType extends AbstractType<Ltree> { +public non-sealed class ReferenceType extends AbstractType<Ltree> { public String getRefType() { return refType; } @@ -103,6 +103,10 @@ public class ReferenceType extends AbstractType<Ltree> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + return; + } gen.writeString(Optional.ofNullable(value).map(Ltree::getSql).orElse("")); } diff --git a/src/main/java/fr/inra/oresing/checker/type/StringType.java b/src/main/java/fr/inra/oresing/checker/type/StringType.java index ec6db59307a4bfe8184dd7051e9a5f373c035947..63e1fec3825770bc701728b8c904e3ffe88377d9 100644 --- a/src/main/java/fr/inra/oresing/checker/type/StringType.java +++ b/src/main/java/fr/inra/oresing/checker/type/StringType.java @@ -22,7 +22,7 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -public class StringType implements FieldType<String> { +public non-sealed class StringType implements FieldType<String> { private final Predicate<String> predicate; private final String pattern; String value = ""; @@ -104,6 +104,10 @@ public class StringType implements FieldType<String> { @Override public void serialize(JsonGenerator gen) throws IOException { + if(value==null){ + gen.writeNull(); + return; + } gen.writeString(value); } diff --git a/src/main/java/fr/inra/oresing/groovy/GroovyContextHelper.java b/src/main/java/fr/inra/oresing/groovy/GroovyContextHelper.java index 6a3625f02c0e87713468f00326b0893f285def9b..43edd8e39b6701b025c35708fb45d83d6208c43e 100644 --- a/src/main/java/fr/inra/oresing/groovy/GroovyContextHelper.java +++ b/src/main/java/fr/inra/oresing/groovy/GroovyContextHelper.java @@ -4,16 +4,15 @@ import com.google.common.collect.ImmutableMap; import fr.inra.oresing.checker.type.FieldType; import fr.inra.oresing.model.Application; import fr.inra.oresing.model.ReferenceValue; +import fr.inra.oresing.model.data.DownloadDatasetQueryNoFilter; import fr.inra.oresing.persistence.DataRepository; import fr.inra.oresing.persistence.DataRow; import fr.inra.oresing.persistence.ReferenceValueRepository; import fr.inra.oresing.rest.OreSiService; -import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.*; -import java.util.stream.Collectors; @Component @Slf4j @@ -49,9 +48,10 @@ public class GroovyContextHelper { Map<String, List<DataRow>> datatypes = new HashMap<>(); Map<String, List<Map<String, Map<String, FieldType>>>> datatypesValues = new HashMap<>(); dataTypes.forEach(dataType -> { - List<DataRow> allByDataType = dataRepository.findAllByDataTypeStream( - DownloadDatasetQuery.buildDownloadDatasetQuery(null, null, dataType, application) - ).collect(Collectors.toList()); + List<DataRow> allByDataType = dataRepository.findAllByDataTypeFlux( + new DownloadDatasetQueryNoFilter(application, dataType, null, null, null) + ).collectList() + .block(); datatypes.put(dataType, allByDataType); allByDataType.stream() .map(datatValues -> datatValues.getValues()) @@ -64,24 +64,24 @@ public class GroovyContextHelper { } /** - * On expose pas directement les entités dans le contexte Groovy mais on contrôle un peu les types - */ - private record ReferenceValueDecorator(ReferenceValue decorated) { + * On expose pas directement les entités dans le contexte Groovy mais on contrôle un peu les types + */ + private record ReferenceValueDecorator(ReferenceValue decorated) { public String getHierarchicalKey() { - return decorated.getHierarchicalKey().getSql(); - } + return decorated.getHierarchicalKey().getSql(); + } - public String getHierarchicalReference() { - return decorated.getHierarchicalReference().getSql(); - } + public String getHierarchicalReference() { + return decorated.getHierarchicalReference().getSql(); + } - public String getNaturalKey() { - return decorated.getNaturalKey().getSql(); - } + public String getNaturalKey() { + return decorated.getNaturalKey().getSql(); + } - public Map<String, Object> getRefValues() { - return decorated.getRefValues().toObjectsExposedInGroovyContext(); - } + public Map<String, Object> getRefValues() { + return decorated.getRefValues().toObjectsExposedInGroovyContext(); } + } } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/model/Configuration.java b/src/main/java/fr/inra/oresing/model/Configuration.java index 7c01d30671f1e055535c72245e47d6b6e524ed29..55f69c784242e8224922e3d38c49c9e0e9034775 100644 --- a/src/main/java/fr/inra/oresing/model/Configuration.java +++ b/src/main/java/fr/inra/oresing/model/Configuration.java @@ -25,36 +25,29 @@ public class Configuration { public final static String HIDDEN_TAG = "__hidden__"; public final static String NO_TAG = "no-tag"; + //@ApiModelProperty(notes = "A description of tags.\n" + + //"Labels can be used in the document to identify groups and enable filters or groupings.", required = false) + public Map<String, Internationalization> tags = new HashMap<>(); + RightsRequestDescription rightsRequest; ////@ApiModelProperty(notes = "The set of requiredAuthorizations of data.authorization section. Fill by aplication", required = false, hidden = true) private List<String> requiredAuthorizationsAttributes; - //@ApiModelProperty(notes = "The version number of the yaml schema used to read the deposited yaml", required = true, example = "1") private int version; - //@ApiModelProperty(notes = "The internationalization description from other sections. Fill by application", required = false, hidden = true) private InternationalizationMap internationalization; - //@ApiModelProperty(notes = "A comment about this yaml", required = false, example = "Adding sites section") private String comment; - //@ApiModelProperty(notes = "An Application description", required = true) private ApplicationDescription application; - - //@ApiModelProperty(notes = "A description of tags.\n" + - //"Labels can be used in the document to identify groups and enable filters or groupings.", required = false) - public Map<String, Internationalization> tags = new HashMap<>(); - - RightsRequestDescription rightsRequest; - //@ApiModelProperty(notes = "A list of references indexed by name. A reference is used to describe other references or data..", required = true) private LinkedHashMap<String, ReferenceDescription> references = new LinkedHashMap<>(); //@ApiModelProperty(notes = "A composite reference allows you to link references according to an ''is in'' link. For example between a city and country reference.\n" + - // "You can define several composite references, and a composite reference can contain only one reference or contain a recursion.\n" + - // "All references used in a datatype.authorization.authorizationscope section must be composite.", required = true) + // "You can define several composite references, and a composite reference can contain only one reference or contain a recursion.\n" + + // "All references used in a datatype.authorization.authorizationscope section must be composite.", required = true) private LinkedHashMap<String, CompositeReferenceDescription> compositeReferences = new LinkedHashMap<>(); //@ApiModelProperty(notes = "An additional file is a file that is dropped onto an application providing additional information described in the configuration.\n" + - // "The deposited files are then associated with \"data\" objects by choosing the authorizationScopes and an interval of dates.", required = false) + // "The deposited files are then associated with \"data\" objects by choosing the authorizationScopes and an interval of dates.", required = false) private LinkedHashMap<String, AdditionalFileDescription> additionalFiles = new LinkedHashMap<>(); //@ApiModelProperty(notes = "A data type describes a set of data representing a cohesive set of measurements or observations. (values can be stored in one csv file format).", required = false) private LinkedHashMap<String, DataTypeDescription> dataTypes = new LinkedHashMap<>(); @@ -71,33 +64,6 @@ public class Configuration { return internationalizationMap; } - @Getter - @Setter - @ToString - public static class RightsRequestDescription { - Internationalization description; - //@ApiModelProperty(notes = "An Rights request is to request rights onto an application providing additional information described in the configuration", required = false) - private Map<String, FieldFormat> format = new HashMap(); - - public static InternationalizationRightsRequestMap getInternationalization(RightsRequestDescription requestDescription) { - final InternationalizationRightsRequestMap internationalizationRightsRequestMap = new InternationalizationRightsRequestMap(); - Map<String, Internationalization> internationalizedFormat = - Maps.transformValues(requestDescription.format, InternationalizationImpl::getInternationalizationName); - internationalizationRightsRequestMap.setFormat(internationalizedFormat); - internationalizationRightsRequestMap.setDescription(requestDescription.description); - return internationalizationRightsRequestMap; - } - } - - - @Getter - @Setter - @ToString - public static class FieldFormat extends InternationalizationImpl { - //@ApiModelProperty(notes = "The description of an information field. \nIf not provided the field is considered as a text field.", required = false) - private CheckerDescription checker; - } - public Optional<CompositeReferenceDescription> getCompositeReferencesUsing(String reference) { return compositeReferences.values().stream() .filter(compositeReferenceDescription -> compositeReferenceDescription.isDependentOfReference(reference)) @@ -220,32 +186,74 @@ public class Configuration { ADD_VARIABLE } + @Getter + @Setter + @ToString + public static class RightsRequestDescription { + Internationalization description; + //@ApiModelProperty(notes = "An Rights request is to request rights onto an application providing additional information described in the configuration", required = false) + private Map<String, FieldFormat> format = new HashMap(); + + public static InternationalizationRightsRequestMap getInternationalization(RightsRequestDescription requestDescription) { + final InternationalizationRightsRequestMap internationalizationRightsRequestMap = new InternationalizationRightsRequestMap(); + Map<String, Internationalization> internationalizedFormat = + Maps.transformValues(requestDescription.format, InternationalizationImpl::getInternationalizationName); + internationalizationRightsRequestMap.setFormat(internationalizedFormat); + internationalizationRightsRequestMap.setDescription(requestDescription.description); + return internationalizationRightsRequestMap; + } + } + + @Getter + @Setter + @ToString + public static class FieldFormat extends InternationalizationImpl { + //@ApiModelProperty(notes = "The description of an information field. \nIf not provided the field is considered as a text field.", required = false) + private CheckerDescription checker; + } + @Getter @Setter @ToString public static class ReferenceDescription extends InternationalizationDisplayImpl { + public List<String> tags = new LinkedList<>(); //@ApiModelProperty(notes = "The separator in csv files", required = false) private char separator = ';'; - //@ApiModelProperty(notes = "The list of columns composing the natural key of a row.", required = true) private List<String> keyColumns = new LinkedList<>(); - //@ApiModelProperty(notes = "The list of columns descriptions.", required = true) private LinkedHashMap<String, ReferenceStaticNotComputedColumnDescription> columns = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "The list of computed columns descriptions. Computed columns are not provided in the CSV but computed line by line when importing.", required = false) private LinkedHashMap<String, ReferenceStaticComputedColumnDescription> computedColumns = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "The list of dynamic columns descriptions. Dynamic columns names refers to an other reference.", required = true) private LinkedHashMap<String, ReferenceDynamicColumnDescription> dynamicColumns = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "The list of validations to perform on this reference.", required = false) private LinkedHashMap<String, LineValidationRuleWithColumnsDescription> validations = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "Possibility that columns of the file are not described", required = false) private boolean allowUnexpectedColumns = false; + public static Map<String, InternationalizationReferenceMap> getInternationalization(LinkedHashMap<String, ReferenceDescription> referenceDescriptionMap) { + Map<String, InternationalizationReferenceMap> internationalizationReferenceMap = new HashMap<>(); + for (Map.Entry<String, ReferenceDescription> entry : referenceDescriptionMap.entrySet()) { + final String reference = entry.getKey(); + final ReferenceDescription referenceDescription = entry.getValue(); + final InternationalizationReferenceMap internationalizationReference = new InternationalizationReferenceMap(); + internationalizationReference.setInternationalizationDisplay(referenceDescription.getInternationalizationDisplay()); + internationalizationReference.setInternationalizationName(referenceDescription.getInternationalizationName()); + internationalizationReference.setInternationalizedColumns(referenceDescription.getInternationalizedColumns()); + Map<String, Internationalization> internationalizedValidations = + Maps.transformValues(referenceDescription.validations, InternationalizationImpl::getInternationalizationName); + internationalizationReference.setInternationalizedValidations(internationalizedValidations); + internationalizationReferenceMap.put(reference, internationalizationReference); + Map<String, Internationalization> internationalizedDynamicColumns = + Maps.transformValues(referenceDescription.dynamicColumns, ReferenceDynamicColumnDescription::getInternationalizationName); + internationalizationReference.setInternationalizedDynamicColumns(internationalizedDynamicColumns); + internationalizationReferenceMap.put(reference, internationalizationReference); + } + return internationalizationReferenceMap; + } + public Set<String> doGetAllColumns() { return doGetAllColumnDescriptions().keySet(); } @@ -254,8 +262,6 @@ public class Configuration { return doGetStaticColumnDescriptions().keySet(); } - public List<String> tags = new LinkedList<>(); - public Map<String, ReferenceColumnDescription> doGetAllColumnDescriptions() { Map<String, ReferenceColumnDescription> allColumnDescriptions = new LinkedHashMap<>(); allColumnDescriptions.putAll(doGetStaticColumnDescriptions()); @@ -273,27 +279,6 @@ public class Configuration { public boolean hasStaticColumn(String column) { return doGetStaticColumns().contains(column); } - - public static Map<String, InternationalizationReferenceMap> getInternationalization(LinkedHashMap<String, ReferenceDescription> referenceDescriptionMap) { - Map<String, InternationalizationReferenceMap> internationalizationReferenceMap = new HashMap<>(); - for (Map.Entry<String, ReferenceDescription> entry : referenceDescriptionMap.entrySet()) { - final String reference = entry.getKey(); - final ReferenceDescription referenceDescription = entry.getValue(); - final InternationalizationReferenceMap internationalizationReference = new InternationalizationReferenceMap(); - internationalizationReference.setInternationalizationDisplay(referenceDescription.getInternationalizationDisplay()); - internationalizationReference.setInternationalizationName(referenceDescription.getInternationalizationName()); - internationalizationReference.setInternationalizedColumns(referenceDescription.getInternationalizedColumns()); - Map<String, Internationalization> internationalizedValidations = - Maps.transformValues(referenceDescription.validations, InternationalizationImpl::getInternationalizationName); - internationalizationReference.setInternationalizedValidations(internationalizedValidations); - internationalizationReferenceMap.put(reference, internationalizationReference); - Map<String, Internationalization> internationalizedDynamicColumns = - Maps.transformValues(referenceDescription.dynamicColumns, ReferenceDynamicColumnDescription::getInternationalizationName); - internationalizationReference.setInternationalizedDynamicColumns(internationalizedDynamicColumns); - internationalizationReferenceMap.put(reference, internationalizationReference); - } - return internationalizationReferenceMap; - } } @Getter @@ -311,18 +296,18 @@ public class Configuration { @Getter @Setter public static abstract class ReferenceStaticColumnDescription extends ReferenceColumnDescription { - public static Set<String> getHeaderName(Set<Map.Entry<String, Configuration.ReferenceStaticColumnDescription>> entries){ + //@ApiModelProperty(notes = "Define a checker to apply for this column on each line of the CSV at import", required = false) + @Nullable + private CheckerDescription checker; + private String headerName; + + public static Set<String> getHeaderName(Set<Map.Entry<String, Configuration.ReferenceStaticColumnDescription>> entries) { return entries.stream() - .map(e-> Optional.ofNullable(e.getValue()) + .map(e -> Optional.ofNullable(e.getValue()) .map(Configuration.ReferenceStaticColumnDescription::getHeaderName) .orElse(e.getKey()) ).collect(Collectors.toSet()); } - - //@ApiModelProperty(notes = "Define a checker to apply for this column on each line of the CSV at import", required = false) - @Nullable - private CheckerDescription checker; - private String headerName; } @Getter @@ -370,8 +355,9 @@ public class Configuration { @ToString public static class AdditionalFileDescription extends InternationalizationImpl { //@ApiModelProperty(notes = "An additional file is a file that is dropped onto an application providing additional information described in the configuration.\n" + - //"The deposited files are then associated with \"data\" objects by choosing the authorizationScopes and an interval of dates.", required = false) + //"The deposited files are then associated with \"data\" objects by choosing the authorizationScopes and an interval of dates.", required = false) private Map<String, AdditionalFileFieldFormat> format = new HashMap(); + public static Map<String, InternationalizationAdditonalFilesMap> getInternationalization(LinkedHashMap<String, AdditionalFileDescription> additionalFilesDescriptionMap) { Map<String, InternationalizationAdditonalFilesMap> internationalizationAdditionalDescriptionMap = new HashMap<>(); for (Map.Entry<String, AdditionalFileDescription> entry : additionalFilesDescriptionMap.entrySet()) { @@ -449,28 +435,33 @@ public class Configuration { @ToString public static class DataTypeDescription extends InternationalizationMapDisplayImpl { + public List<String> tags = new LinkedList<>(); //@ApiModelProperty(notes = "This section describes a binding between a file and the data", required = true) private FormatDescription format; - //@ApiModelProperty(notes = "This section describes the data model, splitting each line of data in variable/components", required = true) private LinkedHashMap<String, VariableDescription> data = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "Some validations rules that will be checked at import. It will allow to make sure a line we import is consistent.", required = true) private LinkedHashMap<String, LineValidationRuleWithVariableComponentsDescription> validations = new LinkedHashMap<>(); - //@ApiModelProperty(notes = "This section defines the variable/components that compose the natural key of a line", required = false) private List<VariableComponentKey> uniqueness = new LinkedList<>(); - //@ApiModelProperty(notes = "This section defines how to migrate the data when a new version of yaml is registered", required = false) private TreeMap<Integer, List<MigrationDescription>> migrations = new TreeMap<>(); - //@ApiModelProperty(notes = "This section defines the autorization model for this dataType, how we define who can access what", required = true) private AuthorizationDescription authorization; - //@ApiModelProperty(notes = "If this section exists, the data file will be store on a repository tree", required = false) private RepositoryDescription repository = null; - public List<String> tags = new LinkedList<>(); + public CheckerType.VariableComponentType getTypeForVariableComponentKey(VariableComponentKey variableComponentKey){ + if(variableComponentKey==null){ + return new CheckerType.VariableComponentType.VariableComponentTextType(); + } + return Optional.ofNullable(getData()) + .map(variables -> variables.get(variableComponentKey.variable())) + .map(variableDescription -> variableDescription.doGetAllVisibleComponentDescriptions()) + .map(components -> components.get(variableComponentKey.component())) + .map(CheckerType.VariableComponentType::getVariableComponentType) + .orElseGet(CheckerType.VariableComponentType.VariableComponentTextType::new); + } public static Map<String, InternationalizationDataTypeMap> getInternationalization(LinkedHashMap<String, DataTypeDescription> dataTypeDescriptionMap) { Map<String, InternationalizationDataTypeMap> internationalizationDataTypeMapMap = new HashMap<>(); @@ -498,6 +489,17 @@ public class Configuration { .map(component -> new VariableComponentKey(variable, component)); }).collect(ImmutableSet.toImmutableSet()); } + + public ImmutableSet<VariableComponentKey> doGetAllVisibleVariableComponents() { + return data.entrySet().stream() + .filter(variableDescription->!variableDescription.getValue().isHidden()) + .flatMap( + variableEntry -> { + String variable = variableEntry.getKey(); + return variableEntry.getValue().doGetAllVisibleComponents().stream() + .map(component -> new VariableComponentKey(variable, component)); + }).collect(ImmutableSet.toImmutableSet()); + } } @Getter @@ -751,6 +753,11 @@ public class Configuration { return doGetAllComponentDescriptions().keySet(); } + public Set<String> doGetAllVisibleComponents() { + return doGetAllVisibleComponentDescriptions() + .keySet(); + } + public Map<String, VariableComponentDescription> doGetAllComponentDescriptions() { Map<String, VariableComponentDescription> allComponentDescriptions = new LinkedHashMap<>(); allComponentDescriptions.putAll(components); @@ -758,6 +765,23 @@ public class Configuration { return allComponentDescriptions; } + public Map<String, VariableComponentDescription> doGetAllVisibleComponentDescriptions() { + Map<String, VariableComponentDescription> allComponentDescriptions = doGetAllComponentDescriptions(); + Set<String> hiddenComponents = components.entrySet().stream() + .filter(componentDescriptionsEntry -> componentDescriptionsEntry.getValue()!=null && componentDescriptionsEntry.getValue().isHidden()) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + Set<String> hiddenComputedComponents = computedComponents.entrySet().stream() + .filter(computedComponentDescriptionsEntry -> computedComponentDescriptionsEntry.getValue()!=null && computedComponentDescriptionsEntry.getValue().isHidden()) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + return doGetAllComponentDescriptions().entrySet() + .stream() + .filter(e->!hiddenComponents.contains(e.getKey())) + .filter(e->!hiddenComputedComponents.contains(e.getKey())) + .collect(HashMap::new, (m,p) -> m.put(p.getKey(),p.getValue()), Map::putAll); + } + public boolean hasComponent(String component) { return doGetAllComponents().contains(component); } @@ -800,6 +824,14 @@ public class Configuration { //@ApiModelProperty(notes = "A component for standardDeviation", required = false) private String standardDeviation = null; + public static String toSQL(String dataType) { + String sql = String.format( + VAR_SQL_DEFAULT_TEMPLATE, + dataType + ); + return sql; + } + public String toSQL(String variableName, String dataType) { String sql = String.format( VAR_SQL_TEMPLATE, @@ -812,14 +844,6 @@ public class Configuration { ); return sql; } - - public static String toSQL(String dataType) { - String sql = String.format( - VAR_SQL_DEFAULT_TEMPLATE, - dataType - ); - return sql; - } } @Getter @@ -833,7 +857,7 @@ public class Configuration { private List<String> tags = new LinkedList<>(); public boolean isHidden() { - return tags.contains(HIDDEN_TAG); + return Optional.ofNullable(tags).map(t -> t.contains(HIDDEN_TAG)).orElse(false); } //private Set<String> tags = Set.of("no-tag"); } @@ -864,13 +888,12 @@ public class Configuration { //@ApiModelProperty(notes = "The name of the checker that must be used", required = true) private CheckerType name; + //@ApiModelProperty(notes = "The params of the checker to configure it. Required for some checkers", required = false) + private CheckerConfigurationDescription params = new CheckerConfigurationDescription(); public void setParams(CheckerConfigurationDescription params) { - this.params = params==null?new CheckerConfigurationDescription():params; + this.params = params == null ? new CheckerConfigurationDescription() : params; } - - //@ApiModelProperty(notes = "The params of the checker to configure it. Required for some checkers", required = false) - private CheckerConfigurationDescription params = new CheckerConfigurationDescription(); } @Getter @@ -927,11 +950,11 @@ public class Configuration { public static class GroovyConfiguration implements fr.inra.oresing.checker.GroovyConfiguration { //@ApiModelProperty(notes = "A groovy expression", required = false, example = ">\n" + - // " String dataType = Arrays.stream(datum.dataType)\n" + - // " .split(\"_\"))\n" + - // " .collect{it.substring(0, 1)}\n" + - // " .join(); " + - //" return application.dataType.contains(dataType);") + // " String dataType = Arrays.stream(datum.dataType)\n" + + // " .split(\"_\"))\n" + + // " .collect{it.substring(0, 1)}\n" + + // " .join(); " + + //" return application.dataType.contains(dataType);") private String expression; //@ApiModelProperty(notes = "The list of references values in database to add to groovy context", required = false) diff --git a/src/main/java/fr/inra/oresing/model/VariableComponentKey.java b/src/main/java/fr/inra/oresing/model/VariableComponentKey.java index 558bd8e117038c19cee5f54359b587aaac778545..672664b2eb921d5f9c0e14b53be8fa5610987ef7 100644 --- a/src/main/java/fr/inra/oresing/model/VariableComponentKey.java +++ b/src/main/java/fr/inra/oresing/model/VariableComponentKey.java @@ -1,19 +1,57 @@ package fr.inra.oresing.model; import fr.inra.oresing.checker.CheckerTarget; +import fr.inra.oresing.checker.Multiplicity; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; import org.apache.commons.lang3.StringUtils; +import java.util.Map; +import java.util.Optional; + +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.FILTER_MISSING_VARIABLE_COMPONENT_FOR_DATATYPE; -public record VariableComponentKey(String variable, String component) implements CheckerTarget { +public record VariableComponentKey(String variable, String component) implements CheckerTarget { private static final String SEPARATOR = "_"; + public VariableComponentKey { + if (variable == null) { + throw new BadDownloadDatasetQuery(BadDownloadDatasetQuery.MISSING_COMPONENT_KEY_VARIABLE); + } + if (component == null) { + throw new BadDownloadDatasetQuery(BadDownloadDatasetQuery.MISSING_COMPONENT_KEY_COMPONENT); + } + } + public static VariableComponentKey parseId(String variableComponentKeyId) { String variable = StringUtils.substringBefore(variableComponentKeyId, SEPARATOR); String component = StringUtils.substringAfter(variableComponentKeyId, SEPARATOR); return new VariableComponentKey(variable, component); } + public Multiplicity test(Configuration.DataTypeDescription dataTypeDescription) { + VariableComponentKey vck = dataTypeDescription.doGetAllVariableComponents().stream() + .filter(variableComponentKey -> variableComponentKey.equals(this)) + .findFirst() + .orElseThrow(() -> new BadDownloadDatasetQuery(FILTER_MISSING_VARIABLE_COMPONENT_FOR_DATATYPE, + Map.of( + "variable", variable(), + "component", component() + ))); + return Optional.of(this) + .map(variableComponentKey -> dataTypeDescription + .getData() + .get(variableComponentKey.variable()) + .getComponents() + .get(variableComponentKey.component()) + ) + .map(Configuration.VariableComponentDescription::getChecker) + .map(Configuration.CheckerDescription::getParams) + .map(Configuration.CheckerConfigurationDescription::getMultiplicity) + .orElse(Multiplicity.ONE); + + } + public String getId() { return variable + SEPARATOR + component; } diff --git a/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuery.java b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuery.java new file mode 100644 index 0000000000000000000000000000000000000000..d0d3929b82182b633659adfac372984979f2dfad --- /dev/null +++ b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuery.java @@ -0,0 +1,53 @@ +package fr.inra.oresing.model.data; + +import fr.inra.oresing.checker.CheckerType; +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.persistence.DataRepository; +import fr.inra.oresing.persistence.SqlPrimitiveType; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; + +import java.util.Locale; +import java.util.Set; + +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.MISSING_COMPONENT_KEY_FOR_SEARCH; + +public sealed interface DownloadDatasetQuery + permits DownloadDatasetQueryNoFilter, DownloadDatasetQueryAdvancedSearch, DownloadDatasetQueryByRowId, DownloadDatasetQuerySimpleSearch { + + Set<VariableComponentOrderBy> variableComponentOrderBy(); + + Set<VariableComponentKey> variableComponentSelects(); + + Application application(); + + String dataType(); + + OutPut outPut(); + + public default Configuration.DataTypeDescription getDatatypeConfiguration() { + return application().getConfiguration().getDataTypes().get(dataType()); + } + + public record OutPut(Locale locale, Long offset, Long limit) { + public OutPut(Locale locale, Long offset, Long limit) { + this.locale = locale == null ? Locale.FRANCE : locale; + this.offset = offset == null ? 0 : offset; + this.limit = limit; + } + } + + public record VariableComponentOrderBy(VariableComponentKey variableComponentKey, DataRepository.Order order, + CheckerType.VariableComponentType sqlType) { + public VariableComponentOrderBy(VariableComponentKey variableComponentKey, DataRepository.Order order, CheckerType.VariableComponentType sqlType) { + + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + this.sqlType = sqlType == null ? new CheckerType.VariableComponentType.VariableComponentTextType() : sqlType; + this.variableComponentKey = variableComponentKey; + this.order = order == null ? DataRepository.Order.ASC : order; + } + } +} diff --git a/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryAdvancedSearch.java b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryAdvancedSearch.java new file mode 100644 index 0000000000000000000000000000000000000000..daf91a5dc6987790b8db7c05fd2558970f2cbd48 --- /dev/null +++ b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryAdvancedSearch.java @@ -0,0 +1,453 @@ +package fr.inra.oresing.model.data; + + +import com.google.common.base.Strings; +import fr.inra.oresing.checker.Multiplicity; +import fr.inra.oresing.checker.type.DateType; +import fr.inra.oresing.model.*; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; + +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.*; + +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.*; + +public record DownloadDatasetQueryAdvancedSearch( + + Application application, + + String dataType, + OutPut outPut, + Set<VariableComponentKey> variableComponentSelects, + Set<VariableComponentFilters> variableComponentFilters, + Set<VariableComponentOrderBy> variableComponentOrderBy +) implements DownloadDatasetQuery { + + public enum FieldType { + date, time, datetime, numeric; + + public static Number convertToNumber(String numericString) { + try { + return Float.valueOf(numericString); + } catch (NumberFormatException nfe) { + return null; + } + } + } + + public sealed interface VariableComponentFilters permits NoVariableComponentFilters, VariableComponentFilterForInterval, VariableComponentFilterSimpleSearch { + default Multiplicity multiplicity() { + return Multiplicity.ONE; + } + } + + public interface ForVariableComponent { + VariableComponentKey variableComponentKey(); + } + + + public sealed interface VariableComponentFilterForInterval extends ForVariableComponent, VariableComponentFilters permits VariableComponentFiltersForIntervalByTemporal, VariableComponentFiltersForIntervalByNumeric { + + } + + public sealed interface VariableComponentFiltersForIntervalByTemporal extends VariableComponentFilterForInterval + permits VariableComponentFiltersForIntervalByDate, VariableComponentFiltersForIntervalByTime, VariableComponentFiltersForIntervalByDateTime { + WithFormatForIntervalDate intervalValues(); + } + + public sealed interface VariableComponentFilterSimpleSearch extends ForVariableComponent, VariableComponentFilters + permits WithFormatForFilterDate, VariableComponentFiltersByNumeric, VariableComponentFiltersByReference, VariableComponentFiltersForWordByPlainText, VariableComponentFiltersForWordByRegexp { + String filter(); + } + + public sealed interface IntervalValues permits IntervalValuesDefault, IntervalValuesNumeric, WithFormatForIntervalDate { + String from(); + + String to(); + } + + public sealed interface WithFormat permits WithFormatForFilterDate, WithFormatForIntervalDate { + String format(); + } + + public sealed interface WithFormatForFilterDate extends DownloadDatasetQueryAdvancedSearch.WithFormat, VariableComponentFilterSimpleSearch { + String filter(); + + default Timestamp getTimeStamp() { + return Optional.ofNullable(filter()) + .map(f -> DateType.valueToDate(DateTimeFormatter.ofPattern(format()), f)) + .map(Timestamp::valueOf) + .orElse(null); + } + + default String getIsoString() { + return Optional.ofNullable(getTimeStamp()) + .map(Timestamp::toLocalDateTime) + .map(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")::format) + .orElse(""); + } + } + + public sealed interface WithFormatForIntervalDate extends DownloadDatasetQueryAdvancedSearch.WithFormat, DownloadDatasetQueryAdvancedSearch.IntervalValues { + default Timestamp fromTimestamp() { + return Optional.ofNullable(from()) + .map(f -> DateType.valueToDate(DateTimeFormatter.ofPattern(format()), f)) + .map(Timestamp::valueOf) + .orElse(Timestamp.from(Instant.MIN)); + } + + default Timestamp toTimestamp() { + return Optional.ofNullable(to()) + .map(f -> DateType.valueToDate(DateTimeFormatter.ofPattern(format()), f)) + .map(Timestamp::valueOf) + .orElse(Timestamp.from(Instant.MAX)); + } + + default String getFromIsoString() { + return Optional.ofNullable(fromTimestamp()) + .map(Timestamp::toLocalDateTime) + .map(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")::format) + .orElse(LocalDateTime.MIN.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + } + + default String getToIsoString() { + return Optional.ofNullable(toTimestamp()) + .map(Timestamp::toLocalDateTime) + .map(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")::format) + .orElse(LocalDateTime.MAX.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + } + } + + public record NoVariableComponentFilters() implements VariableComponentFilters { + } + + public record VariableComponentFiltersForIntervalByDate( + VariableComponentKey variableComponentKey, + WithFormatForIntervalDate intervalValues, + Multiplicity multiplicity + ) implements VariableComponentFiltersForIntervalByTemporal { + public VariableComponentFiltersForIntervalByDate { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + } + } + + public record VariableComponentFiltersForIntervalByTime( + VariableComponentKey variableComponentKey, + DownloadDatasetQueryAdvancedSearch.IntervalValuesTime intervalValues, + Multiplicity multiplicity + ) implements VariableComponentFiltersForIntervalByTemporal { + public VariableComponentFiltersForIntervalByTime { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (intervalValues == null) { + throw new BadDownloadDatasetQuery(MISSING_INTERVAL_VALUE); + } + } + } + + public record VariableComponentFiltersForIntervalByDateTime( + VariableComponentKey variableComponentKey, + DownloadDatasetQueryAdvancedSearch.IntervalValuesDateTime intervalValues, + Multiplicity multiplicity + ) implements VariableComponentFiltersForIntervalByTemporal { + public VariableComponentFiltersForIntervalByDateTime { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (intervalValues == null) { + throw new BadDownloadDatasetQuery(MISSING_INTERVAL_VALUE); + } + } + } + + public record VariableComponentFiltersForIntervalByNumeric( + VariableComponentKey variableComponentKey, + DownloadDatasetQueryAdvancedSearch.IntervalValuesNumeric intervalValues, + Multiplicity multiplicity + ) implements VariableComponentFilterForInterval { + public VariableComponentFiltersForIntervalByNumeric { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (intervalValues == null) { + throw new BadDownloadDatasetQuery(MISSING_INTERVAL_VALUE); + } + } + } + + public record VariableComponentFiltersForWordByRegexp( + VariableComponentKey variableComponentKey, + String filter, + Multiplicity multiplicity + ) implements VariableComponentFilterSimpleSearch { + public VariableComponentFiltersForWordByRegexp { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_FILTER); + } + } + } + + public record VariableComponentFiltersForWordByPlainText( + VariableComponentKey variableComponentKey, + String filter, + Multiplicity multiplicity + ) implements VariableComponentFilterSimpleSearch { + public VariableComponentFiltersForWordByPlainText { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_FILTER); + } + } + } + + public record VariableComponentFiltersByDate( + VariableComponentKey variableComponentKey, + String format, + String filter, + Multiplicity multiplicity) implements WithFormatForFilterDate { + public VariableComponentFiltersByDate { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_FILTER); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FILTER); + } + } + } + + public record VariableComponentFiltersByTime( + VariableComponentKey variableComponentKey, + String format, + String filter, + Multiplicity multiplicity + ) implements WithFormatForFilterDate { + public VariableComponentFiltersByTime { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_FILTER); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FILTER); + } + } + } + + public record VariableComponentFiltersByDateTime( + VariableComponentKey variableComponentKey, String format, + String filter, + Multiplicity multiplicity + ) implements WithFormatForFilterDate { + public VariableComponentFiltersByDateTime { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_FILTER); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FILTER); + } + } + } + + public record VariableComponentFiltersByReference( + VariableComponentKey variableComponentKey, + String filter, + Multiplicity multiplicity + ) implements VariableComponentFilterSimpleSearch { + public VariableComponentFiltersByReference { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FILTER); + } + } + } + + public record VariableComponentFiltersByNumeric( + VariableComponentKey variableComponentKey, + String filter, + Multiplicity multiplicity + ) implements VariableComponentFilterSimpleSearch { + public VariableComponentFiltersByNumeric { + if (variableComponentKey == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } + if (Strings.isNullOrEmpty(filter)) { + throw new BadDownloadDatasetQuery(MISSING_FILTER); + } + } + } + + public record IntervalValuesDate( + String from, + String to, + String format + ) implements WithFormatForIntervalDate { + + public IntervalValuesDate { + LocalDate fromDate = null; + LocalDate toDate = null; + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_INTERVAL_VALUE); + } + if (from != null) { + try { + fromDate = LocalDate.from(DateTimeFormatter.ofPattern(format).parse(from)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_START_DATE); + } + } + if (to != null) { + try { + toDate = LocalDate.from(DateTimeFormatter.ofPattern(format).parse(to)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_END_DATE); + } + } + if (fromDate != null && toDate != null && toDate.isBefore(fromDate)) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_BAD_RANGE_FOR_DATES, Map.of("from", from, "to", to)); + } + } + + } + + public record IntervalValuesTime( + String from, + String to, + String format + ) implements WithFormatForIntervalDate { + + + public IntervalValuesTime { + LocalTime fromDate = null; + LocalTime toDate = null; + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_INTERVAL_VALUE); + } + if (from != null) { + try { + fromDate = LocalTime.from(DateTimeFormatter.ofPattern(format).parse(from)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_START_TIME); + } + } + if (to != null) { + try { + toDate = LocalTime.from(DateTimeFormatter.ofPattern(format).parse(to)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_END_TIME); + } + } + if (fromDate != null && toDate != null && toDate.isBefore(fromDate)) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_BAD_RANGE_FOR_TIMES, Map.of("from", from, "to", to)); + } + } + + } + + public record IntervalValuesDateTime( + String from, + String to, + String format + ) implements WithFormatForIntervalDate { + + public IntervalValuesDateTime { + LocalDateTime fromDate = null; + LocalDateTime toDate = null; + if (Strings.isNullOrEmpty(format)) { + throw new BadDownloadDatasetQuery(MISSING_FORMAT_FOR_INTERVAL_VALUE); + } + if (from != null) { + try { + fromDate = LocalDateTime.from(DateTimeFormatter.ofPattern(format).parse(from)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_START_DATE_TIME); + } + } + if (to != null) { + try { + toDate = LocalDateTime.from(DateTimeFormatter.ofPattern(format).parse(to)); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_END_DATE_TIME); + } + } + if (fromDate != null && toDate != null && toDate.isBefore(fromDate)) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_BAD_RANGE_FOR_DATE_TIMES, Map.of("from", from, "to", to)); + } + } + + } + + public record IntervalValuesNumeric( + String from, + String to) implements DownloadDatasetQueryAdvancedSearch.IntervalValues { + public IntervalValuesNumeric { + Float fromNumeric = null; + Float toNumeric = null; + if (from != null) { + try { + fromNumeric = Float.parseFloat(from); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_START_NUMERIC); + } + if (to != null) { + try { + toNumeric = Float.parseFloat(to); + } catch (DateTimeParseException e) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_FOR_END_NUMERIC); + } + if (fromNumeric != null && toNumeric != null && fromNumeric.compareTo(toNumeric) > 0) { + throw new BadDownloadDatasetQuery(FILTER_BAD_FORMAT_BAD_RANGE_FOR_NUMERICS, Map.of("from", from, "to", to)); + } + } + + } + } + + private static Number toNumeric(String value) { + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return null; + } + } + + public Number fromFromNumeric() { + return Optional.ofNullable(from()) + .map(IntervalValuesNumeric::toNumeric) + .orElse(Integer.MIN_VALUE); + } + + public Number fromToNumeric() { + return Optional.ofNullable(to()) + .map(IntervalValuesNumeric::toNumeric) + .orElse(Integer.MAX_VALUE); + } + } + + public record IntervalValuesDefault( + String from, + String to) implements DownloadDatasetQueryAdvancedSearch.IntervalValues { + } +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryByRowId.java b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryByRowId.java new file mode 100644 index 0000000000000000000000000000000000000000..801ce8c71dca90c442d0eb0550d53ade6200e781 --- /dev/null +++ b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryByRowId.java @@ -0,0 +1,44 @@ +package fr.inra.oresing.model.data; + + +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; +import org.apache.logging.log4j.util.Strings; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.MISSING_ID_FOR_UUID; + +public record DownloadDatasetQueryByRowId( + Application application, + String dataType, + OutPut outPut, + Set<VariableComponentKey> variableComponentSelects, + Set<VariableComponentOrderBy> variableComponentOrderBy, + + Set<DataRowIds> rowIds) implements DownloadDatasetQuery { + public DownloadDatasetQueryByRowId { + Objects.requireNonNull(application, "You must provide a valide application"); + if(!Strings.isNotEmpty(dataType)){ + throw new IllegalArgumentException("You must provide a valide datatype"); + } + Optional.of(application) + .map(Application::getConfiguration) + .map(Configuration::getDataTypes) + .filter(dataTypes->dataTypes.containsKey(dataType)) + .orElseThrow(()->new IllegalArgumentException("Datatype must be declared in configuration")); + } + + public record DataRowIds(UUID id) { + public DataRowIds { + if (id == null) { + throw new BadDownloadDatasetQuery(MISSING_ID_FOR_UUID); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryNoFilter.java b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryNoFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..2e770324cc705466bedab3b18f1c954810d0e383 --- /dev/null +++ b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQueryNoFilter.java @@ -0,0 +1,15 @@ +package fr.inra.oresing.model.data; + +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.VariableComponentKey; + +import java.util.Set; + +public record DownloadDatasetQueryNoFilter( + Application application, + String dataType, + OutPut outPut, + Set<VariableComponentKey> variableComponentSelects, + Set<VariableComponentOrderBy> variableComponentOrderBy +) implements DownloadDatasetQuery { +} diff --git a/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuerySimpleSearch.java b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuerySimpleSearch.java new file mode 100644 index 0000000000000000000000000000000000000000..098e5f8ee0d4df0e7ede1a672a3b1639887d8b18 --- /dev/null +++ b/src/main/java/fr/inra/oresing/model/data/DownloadDatasetQuerySimpleSearch.java @@ -0,0 +1,71 @@ +package fr.inra.oresing.model.data; + + +import com.google.common.base.Strings; +import fr.inra.oresing.model.*; +import fr.inra.oresing.persistence.DataRepository; +import fr.inra.oresing.persistence.Ltree; +import fr.inra.oresing.persistence.SqlSchemaForApplication; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.*; +import java.util.stream.Collectors; + +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.*; + +public record DownloadDatasetQuerySimpleSearch( + + Application application, + String dataType, + OutPut outPut, + Set<VariableComponentKey> variableComponentSelects, + Set<VariableComponentOrderBy> variableComponentOrderBy, + + Set<AuthorizationDescription>authorizationDescriptions) implements DownloadDatasetQuery { + + public record RequiredAuthorization(String compositereferenceLabel, Ltree path) { + } + + public record IntervalValues(String from, String to) { + + } + + public record AuthorizationDescription(IntervalValues timeScope, + List<List<RequiredAuthorization>> requiredAuthorizations) { + /* public static AuthorizationDescription of(Map map) { + Map<String, String> timeScopeMap = (Map<String, String>) map.get("timeScope"); + IntervalValues intervalValues = new IntervalValues(timeScopeMap.get("from"), timeScopeMap.get("to")); + List<List<RequiredAuthorization>> requiredAuthorizations1 = ((List<Map<String, String>>) map.get("requiredAuthorizations")) + .stream() + .map(ra -> ra.entrySet().stream() + .map(entry -> new RequiredAuthorization(entry.getKey(), Ltree.fromSql(entry.getValue()))) + .toList() + ) + .toList(); + return new AuthorizationDescription(intervalValues, requiredAuthorizations1); + }*/ + + public List<Authorization> toAuthorization(Application application, SqlSchemaForApplication schema) { + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String sqlStart = schema.getSqlIdentifier() + ".isauthorized(data.authorization, array["; + String sqlEnd = "]::" + schema.getSqlIdentifier() + ".authorization[])"; + LocalDateTimeRange localDateTimeRange = timeScope == null ? LocalDateTimeRange.always() : LocalDateTimeRange.getTimeScope( + (timeScope.from() == null ? null : LocalDate.parse(timeScope.from(), dateFormatter)), + (timeScope.to() == null ? null : LocalDate.parse(timeScope.to(), dateFormatter))); + return Optional.ofNullable(requiredAuthorizations).orElse(List.of()).stream() + .map(requiredAuthorization -> new Authorization( + List.of(), + requiredAuthorization.stream() + .collect(Collectors.toMap(RequiredAuthorization::compositereferenceLabel, RequiredAuthorization::path)), + localDateTimeRange + )) + .collect(Collectors.toList()); + + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/persistence/DataRepository.java b/src/main/java/fr/inra/oresing/persistence/DataRepository.java index 20a59236f33b314677f41dac190d817d43a16b72..db5d66adbe7bb83eccb70ca62f50a91f80f86754 100644 --- a/src/main/java/fr/inra/oresing/persistence/DataRepository.java +++ b/src/main/java/fr/inra/oresing/persistence/DataRepository.java @@ -1,26 +1,20 @@ package fr.inra.oresing.persistence; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import fr.inra.oresing.model.Application; -import fr.inra.oresing.model.Configuration; import fr.inra.oresing.model.Data; -import fr.inra.oresing.model.VariableComponentKey; -import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; +import fr.inra.oresing.model.data.DownloadDatasetQuery; +import fr.inra.oresing.persistence.requestBuilder.datatype.DataTypeRequestBuilder; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.hateoas.server.ExposesResourceFor; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Flux; import java.sql.PreparedStatement; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import java.util.stream.Stream; @Component @@ -40,9 +34,9 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla @Override protected String getUpsertQuery() { return "INSERT INTO " + getTable().getSqlIdentifier() + "(id, application, dataType, rowId, \"authorization\", uniqueness, refsLinkedTo, dataValues, binaryFile) \n" + - "SELECT id, application, dataType, rowId, \"authorization\", uniqueness, refsLinkedTo, dataValues, binaryFile FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json) " - + " ON CONFLICT (dataType, datagroup, uniqueness) DO UPDATE SET updateDate=current_timestamp, application=EXCLUDED.application, dataType=EXCLUDED.dataType, rowId=EXCLUDED.rowId, \"authorization\"=EXCLUDED.\"authorization\", refsLinkedTo=EXCLUDED.refsLinkedTo, dataValues=EXCLUDED.dataValues, binaryFile=EXCLUDED.binaryFile" - + " RETURNING id"; + "SELECT id, application, dataType, rowId, \"authorization\", uniqueness, refsLinkedTo, dataValues, binaryFile FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json) " + + " ON CONFLICT (dataType, datagroup, uniqueness) DO UPDATE SET updateDate=current_timestamp, application=EXCLUDED.application, dataType=EXCLUDED.dataType, rowId=EXCLUDED.rowId, \"authorization\"=EXCLUDED.\"authorization\", refsLinkedTo=EXCLUDED.refsLinkedTo, dataValues=EXCLUDED.dataValues, binaryFile=EXCLUDED.binaryFile" + + " RETURNING id"; } @Override @@ -50,18 +44,11 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla return Data.class; } - public Stream<DataRow> findAllByDataTypeStream(DownloadDatasetQuery downloadDatasetQuery) { - final DownloadDatasetQueryBuilder downloadDatasetQueryBuilder = new DownloadDatasetQueryBuilder(downloadDatasetQuery); - Stream result; - if (CollectionUtils.isEmpty(downloadDatasetQuery.getAuthorizationDescriptions())) { - String query = downloadDatasetQueryBuilder.buildAdvancedSearchQuery(); - result = getNamedParameterJdbcTemplate().queryForStream(query, downloadDatasetQueryBuilder.getParamSource(), getJsonRowMapper()); - } else { - String query = downloadDatasetQueryBuilder.buildSimpleSearchQuery(); - downloadDatasetQueryBuilder.getParamSource().addValue("application", downloadDatasetQuery.getApplication().getId()); - downloadDatasetQueryBuilder.getParamSource().addValue("datatype", downloadDatasetQuery.getDataType()); - result = getNamedParameterJdbcTemplate().queryForStream(query, downloadDatasetQueryBuilder.getParamSource(), getJsonRowMapper()); - }return result; + public Flux<DataRow> findAllByDataTypeFlux(DownloadDatasetQuery downloadDatasetQuery) { + Stream result; + DataTypeRequestBuilder.SqlRequest sqlRequest = DataTypeRequestBuilder.buildSelectRequest(downloadDatasetQuery); + result = getNamedParameterJdbcTemplate().queryForStream(sqlRequest.sql(), sqlRequest.parameterSource(), getJsonRowMapper()); + return Flux.<DataRow>fromStream(result); } final String buildCursorName(String query, String[] parameterNames) { @@ -69,40 +56,19 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla } public List<UUID> deleteDataType(DownloadDatasetQuery downloadDatasetQuery) { - final DownloadDatasetQueryBuilder downloadDatasetQueryBuilder = new DownloadDatasetQueryBuilder(downloadDatasetQuery); - if (CollectionUtils.isEmpty(downloadDatasetQuery.getAuthorizationDescriptions())) { - String query = downloadDatasetQueryBuilder.buildDeleteAdvancedSearchQuery(); - List<UUID> result = getNamedParameterJdbcTemplate().queryForList(query, convertMapsqlparameterSourcetoMap(downloadDatasetQueryBuilder.getParamSource()), UUID.class); - return result; - } else { - String query = downloadDatasetQueryBuilder.buildDeleteSimpleSearchQuery(); - downloadDatasetQueryBuilder.getParamSource().addValue("application", downloadDatasetQuery.getApplication().getId()); - downloadDatasetQueryBuilder.getParamSource().addValue("datatype", downloadDatasetQuery.getDataType()); - List<UUID> result = getNamedParameterJdbcTemplate().queryForList(query, convertMapsqlparameterSourcetoMap(downloadDatasetQueryBuilder.getParamSource()), UUID.class); - return result; - } + DataTypeRequestBuilder.SqlRequest sqlRequest = DataTypeRequestBuilder.buildDeleteRequest(downloadDatasetQuery); + return getNamedParameterJdbcTemplate().queryForList(sqlRequest.sql(), sqlRequest.parameterSource(), UUID.class); } public int removeByFileId(UUID fileId) { String query = "DELETE FROM " + getTable().getSqlIdentifier() + - "\n WHERE binaryfile::text in( :binaryFile)"; + "\n WHERE binaryfile::text in( :binaryFile)"; final ImmutableMap<String, List<String>> params = ImmutableMap.of("binaryFile", List.of(fileId.toString())); final int unPublishdLines = getNamedParameterJdbcTemplate().update(query, params); flush(); return unPublishdLines; } - public String getSqlToMergeData(DownloadDatasetQuery downloadDatasetQuery) { - String dataType = downloadDatasetQuery.getDataType(); - Preconditions.checkArgument(getApplication().getDataType().contains(dataType), "pas de type de données " + dataType + " dans l'application " + getApplication()); - String applicationId = getApplication().getId().toString(); - String sql = " SELECT \n\trowId, \n\tjsonb_object_agg(datavalues) AS dataValues, \n\tjsonb_object_agg(refsLinkedTo) AS refsLinkedTo" - + " \nFROM " + getTable().getSqlIdentifier() - + " \nWHERE application = '" + applicationId + "'::uuid \n\tAND dataType = '" + dataType + "'" - + " \nGROUP BY rowId"; - return sql; - } - public int migrate(String dataType, String dataGroup, Map<String, Map<String, String>> variablesToAdd, Map<String, Map<String, Set<UUID>>> refsLinkedToToAdd) { String setRefsLinkedToClause; if (refsLinkedToToAdd.isEmpty()) { @@ -113,9 +79,9 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla } String dataValuesAsJson = getJsonRowMapper().toJson(variablesToAdd); String sql = " UPDATE " + getTable().getSqlIdentifier() - + " SET dataValues = dataValues || '" + dataValuesAsJson + "'::jsonb" - + setRefsLinkedToClause - + " WHERE application = :applicationId::uuid AND dataType = :dataType AND dataGroup = :dataGroup"; + + " SET dataValues = dataValues || '" + dataValuesAsJson + "'::jsonb" + + setRefsLinkedToClause + + " WHERE application = :applicationId::uuid AND dataType = :dataType AND dataGroup = :dataGroup"; MapSqlParameterSource sqlParams = new MapSqlParameterSource("applicationId", getApplication().getId()) .addValue("dataType", dataType) .addValue("dataGroup", dataGroup); @@ -125,10 +91,10 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla public List<Uniqueness> findStoredUniquenessForDatatype(Application application, String dataType) { String query = "select 'fr.inra.oresing.persistence.Uniqueness' as \"@class\",uniqueness as json from " + getTable().getSqlIdentifier() - + " WHERE application = :applicationId::uuid AND dataType = :dataType"; + + " WHERE application = :applicationId::uuid AND dataType = :dataType"; MapSqlParameterSource sqlParams = new MapSqlParameterSource("applicationId", getApplication().getId()) .addValue("dataType", dataType); - return getNamedParameterJdbcTemplate().query(query, sqlParams, new JsonRowMapper<Uniqueness>(new ObjectMapper())); + return getNamedParameterJdbcTemplate().query(query, sqlParams, new JsonRowMapper<Uniqueness>()); } public void updateConstraintForeignData(List<UUID> uuids) { @@ -149,296 +115,6 @@ public class DataRepository extends JsonTableInApplicationSchemaRepositoryTempla .forEachRemaining(uuidsByBatch -> getNamedParameterJdbcTemplate().execute(sql, ImmutableMap.of("ids", uuidsByBatch), PreparedStatement::execute)); } - public class DownloadDatasetQueryBuilder { - private AtomicInteger atomicInteger = new AtomicInteger(); - - public MapSqlParameterSource getParamSource() { - return paramSource; - } - - private MapSqlParameterSource paramSource = new MapSqlParameterSource(); - DownloadDatasetQuery downloadDatasetQuery; - Application application; - - SqlTable table; - - public DownloadDatasetQueryBuilder(DownloadDatasetQuery downloadDatasetQuery) { - this.downloadDatasetQuery = downloadDatasetQuery; - this.paramSource = new MapSqlParameterSource("applicationId", downloadDatasetQuery.getApplication().getId()); - this.application = this.downloadDatasetQuery == null ? null : this.downloadDatasetQuery.getApplication(); - } - - public String addArgumentAndReturnSubstitution(Object value) { - int i = this.atomicInteger.incrementAndGet(); - String paramName = String.format("arg%d", i); - paramSource.addValue(paramName, value); - return String.format(":%s", paramName); - } - - String addOrderBy(String query) { - Set<DownloadDatasetQuery.VariableComponentOrderBy> variableComponentKeySet = new LinkedHashSet<>(); - String orderBy = Optional.ofNullable(downloadDatasetQuery.getVariableComponentOrderBy()) - .filter(vck -> !CollectionUtils.isEmpty(vck)) - .orElseGet(() -> { - - final Configuration.AuthorizationDescription authorization = downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()).getAuthorization(); - if (authorization != null && authorization.getTimeScope() != null) { - variableComponentKeySet.add( - new DownloadDatasetQuery.VariableComponentOrderBy( - authorization.getTimeScope(), - Order.ASC) - ); - } - if (authorization != null) { - authorization.getAuthorizationScopes().values() - .stream() - .map(Configuration.AuthorizationScopeDescription::getVariableComponentKey) - .forEach(vck -> variableComponentKeySet.add( - new DownloadDatasetQuery.VariableComponentOrderBy(vck, Order.ASC) - )); - } - return variableComponentKeySet; - } - ).stream() - .map(vck -> { - String format; - if ("numeric".equals(vck.type)) { - format = "(nullif(datavalues#>>'{\"%s\",\"%s\"}', ''))::numeric"; - } else { - format = "datavalues #>'{\"%s\",\"%s\"}'"; - } - - return String.format( - format, - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getVariable()), - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getComponent()), - vck.getOrder()); - }) - .filter(sorted -> !Strings.isNullOrEmpty(sorted)) - .collect(Collectors.joining(", ")); - return Strings.isNullOrEmpty(orderBy) ? query : String.format("%s \nORDER by %s", query, orderBy); - } - - String filterBy(String query) { - String filter = ""; - Set<String> rowIds = downloadDatasetQuery.getRowIds(); - final Set<String> uuids2 = Optional.ofNullable(rowIds) - .filter(uuids1 -> !rowIds.isEmpty()) - .orElse(null); - if (rowIds != null && !rowIds.isEmpty()) { - final String uuidArgument = addArgumentAndReturnSubstitution(uuids2); - filter = String.format("\n my_data.rowid in (%s) \n", uuidArgument); - } else { - Set<DownloadDatasetQuery.VariableComponentFilters> variableComponentKeySet = new LinkedHashSet<>(); - filter = Optional.ofNullable(downloadDatasetQuery.getVariableComponentFilters()) - .filter(vck -> !CollectionUtils.isEmpty(vck)) - .orElseGet(LinkedHashSet::new) - .stream() - .map(vck -> getFormat(vck)) - .filter(f -> !Strings.isNullOrEmpty(f)) - .collect(Collectors.joining(" \nAND ")); - } - return Strings.isNullOrEmpty(filter) ? query : String.format("%s \nWHERE %s", query, filter); - } - - private String getFormat(DownloadDatasetQuery.VariableComponentFilters vck) { - boolean isRegExp = vck.isRegExp != null && vck.isRegExp; - List<String> filters = new LinkedList<>(); - if (!Strings.isNullOrEmpty(vck.filter)) { - filters.add(String.format( - "my_data.datavalues #> '{\"%s\",\"%s\"}' @@ ('$ like_regex \"'||%s||'\"')::jsonpath", - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getVariable()), - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getComponent()), - /*String.format(isRegExp ? "~ %s" : "ilike '%%'||%s||'%%'", */ - addArgumentAndReturnSubstitution(vck.getFilter())//) - ) - ); - - } else if (vck.intervalValues != null && List.of("date", "time", "datetime").contains(vck.type)) { - if (!Strings.isNullOrEmpty(vck.intervalValues.from) || !Strings.isNullOrEmpty(vck.intervalValues.to)) { - filters.add( - String.format( - "my_data.datavalues #> '{\"%1$s\",\"%2$s\"}'@@ ('$ >= \"date:'||%3$s||'\" && $ <= \"date:'||%4$s||'Z\"')::jsonpath", - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getVariable()), - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getComponent()), - addArgumentAndReturnSubstitution(Strings.isNullOrEmpty(vck.intervalValues.from) ? "0" : vck.intervalValues.from), - addArgumentAndReturnSubstitution(Strings.isNullOrEmpty(vck.intervalValues.to) ? "9" : vck.intervalValues.to) - ) - ); - } - } else if (vck.intervalValues != null && "numeric".equals(vck.type)) { - if (!Strings.isNullOrEmpty(vck.intervalValues.from) || !Strings.isNullOrEmpty(vck.intervalValues.to)) { - //datavalues #> '{"t","value"}'@@ '$. double() >= 1 && $. double() <= 2' - List<String> filter = new LinkedList<>(); - if (!Strings.isNullOrEmpty(vck.intervalValues.from)) { - filter.add(String.format( - "$. double() >= '||%s||'", - addArgumentAndReturnSubstitution(vck.intervalValues.from) - ) - ); - } - if (!Strings.isNullOrEmpty(vck.intervalValues.to)) { - filter.add(String.format( - "$. double() <= '||%s||'", - addArgumentAndReturnSubstitution(vck.intervalValues.to) - ) - ); - } - filters.add( - String.format("my_data.datavalues #> '{\"%s\",\"%s\"}'@@ ('%s')::jsonpath", - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getVariable()), - JsonTableInApplicationSchemaRepositoryTemplate.escapeSql(vck.getComponent()), - String.join(" && ", filter) - ) - ); - } - } - return filters.stream() - .filter(filter -> !Strings.isNullOrEmpty(filter)) - .collect(Collectors.joining(" AND ")); - } - - private String filterHiddenVariables() { - return getHiddenVariables().stream() - .map(List::of) - .map(this::buildDeleteJsonPathSql) - .collect(Collectors.joining(" ")); - } - - private String filterHiddenComponents() { - return getHiddenComponents().stream() - .map(vc -> List.of(vc.variable(), vc.component())) - .map(this::buildDeleteJsonPathSql) - .collect(Collectors.joining(" ")); - } - - private String buildDeleteJsonPathSql(List<String> pathes) { - return pathes.stream().collect(Collectors.joining(",", "#- '{", "}'")); - } - - - public List<String> getHiddenVariables() { - return application.getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()).getData().entrySet().stream() - .filter(entry -> entry.getValue().isHidden()) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - } - - public List<VariableComponentKey> getHiddenComponents() { - return application.getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()).getData().entrySet().stream() - .map(entryVariable -> { - String variableName = entryVariable.getKey(); - LinkedHashMap<String, Configuration.VariableComponentDescription> components = new LinkedHashMap<>(); - components.putAll(entryVariable.getValue().getComponents()); - components.putAll(entryVariable.getValue().getComputedComponents()); - return components.entrySet().stream() - .filter(entry -> entry.getValue() != null && entry.getValue().isHidden()) - .map(Map.Entry::getKey) - .map(componentName -> new VariableComponentKey(variableName, componentName)) - .collect(Collectors.toList()); - }) - .flatMap(List::stream) - .collect(Collectors.toList()); - } - - public String buildAdvancedSearchQuery() { - String toMergeDataGroupsQuery = getSqlToMergeData(downloadDatasetQuery); - String filterHiddenVariable = filterHiddenVariables(); - String filterHiddenComponents = filterHiddenComponents(); - String query = "WITH my_data AS (\n" + toMergeDataGroupsQuery + "\n)" + - "\n SELECT '" + DataRow.class.getName() + "' AS \"@class\", " + - "\njsonb_build_object(" + - "\n\t'rowNumber', row_number() over (), " + - "\n\t'totalRows', count(*) over (), " + - "\n\t'rowId', rowId, " + - "\n\t'values', dataValues " + filterHiddenVariable + " " + filterHiddenComponents + ", " + - "\n\t'refsLinkedTo', refsLinkedTo" + filterHiddenVariable + " " + filterHiddenComponents + " " + - "\n) AS json" - + " \nFROM my_data "; - query = filterBy(query); - query = addOrderBy(query); - if (downloadDatasetQuery.getOffset() != null && downloadDatasetQuery.getLimit() >= 0) { - query = String.format("%s \nOFFSET %d ROWS", query, downloadDatasetQuery.getOffset()); - } - if (downloadDatasetQuery.getLimit() != null && downloadDatasetQuery.getLimit() >= 0) { - query = String.format("%s \nFETCH FIRST %d ROW ONLY", query, downloadDatasetQuery.getLimit()); - } - return query; - } - - public String buildSimpleSearchQuery() { - String filter = buildSimpleSearchFilter(); - String filterHiddenVariable = filterHiddenVariables(); - String filterHiddenComponents = filterHiddenComponents(); - String query = "SELECT \n" + - " '" + DataRow.class.getName() + "' AS \"@class\",\n" + - " jsonb_build_object(\n" + - " 'rowNumber', row_number() over (),\n" + - " 'totalRows', count(*) over (), \n" + - " 'rowId', rowId, \n" + - " 'values', jsonb_object_agg(dataValues " + filterHiddenVariable + " " + filterHiddenComponents + "), \n " + - " 'refsLinkedTo', jsonb_object_agg(refsLinkedTo " + filterHiddenVariable + " " + filterHiddenComponents + ")\n " + - " ) AS \"json\" \n" + - "FROM " + getSchema().getSqlIdentifier() + ".data\n" + - "WHERE \n" + - " application=:application::uuid\n" + - " AND data.datatype=:datatype\n"; - query = String.format("%s\n%s\nGROUP BY data.rowid\n", query, filter); - if (downloadDatasetQuery.getOffset() != null && downloadDatasetQuery.getLimit() >= 0) { - query = String.format("%s \nOFFSET %d ROWS", query, downloadDatasetQuery.getOffset()); - } - if (downloadDatasetQuery.getLimit() != null && downloadDatasetQuery.getLimit() >= 0) { - query = String.format("%s \nFETCH FIRST %d ROW ONLY", query, downloadDatasetQuery.getLimit()); - } - return query; - } - - public String buildDeleteAdvancedSearchQuery() { - String toMergeDataGroupsQuery = getSqlToMergeData(downloadDatasetQuery); - String query = "WITH my_data AS (\n" + toMergeDataGroupsQuery + "\n)" + - "\n DELETE FROM "+ getTable().getSqlIdentifier()+ " t\n"+ - "using my_data "; - String filter = filterBy(query); - boolean noWhere = filter.equals(query); - String join = String.format("\n%s my_data.rowid = t.rowid", noWhere?" WHERE ": "AND "); - query = filter +join; - query += "\nRETURNING id"; - return query; - } - - public String buildDeleteSimpleSearchQuery() { - String filter = buildSimpleSearchFilter(); - String query = "DELETE FROM " + getSchema().getSqlIdentifier() + ".data\n" + - "WHERE \n" + - " application=:application::uuid\n" + - " AND data.datatype=:datatype\n"; - query = String.format("%s\n%s\nRETURNING id\n", query, filter); - return query; - } - - private String buildSimpleSearchFilter() { - if (CollectionUtils.isEmpty(downloadDatasetQuery.getAuthorizationDescriptions())) { - return ""; - } - String sqlStart = "data.authorization @> "; - String sqlEnd = "::" + getSchema().getSqlIdentifier() + ".authorization[]"; - final String sql = downloadDatasetQuery.getAuthorizationDescriptions().stream() - .map(authorizationdescription -> authorizationdescription.toAuthorization(downloadDatasetQuery.getApplication(), getSchema())) - .filter(authorizations -> !authorizations.isEmpty()) - .map(authorizations -> { - String authorizationsSql = authorizations.stream() - .map(authorization -> authorization.toSQL(downloadDatasetQuery.getApplication().getConfiguration().getRequiredAuthorizationsAttributes())) - .map(expression -> String.format(expression, getSchema().getSqlIdentifier())) - .collect(Collectors.joining(",", "ARRAY[\n", "\n]")); - return new StringBuilder(sqlStart) - .append(authorizationsSql) - .append(sqlEnd); - }) - .collect(Collectors.joining(") OR (", "\n(", ")")); - return String.format("\nAND (%s) ", sql); - } - } - public enum Order { ASC, DESC } diff --git a/src/main/java/fr/inra/oresing/persistence/JsonRowMapper.java b/src/main/java/fr/inra/oresing/persistence/JsonRowMapper.java index 7bb5a019bb0e44a1e5cfec7556d9451a93bc48f2..d6bfb2879e674bd989f3fa428ae193571c1db681 100644 --- a/src/main/java/fr/inra/oresing/persistence/JsonRowMapper.java +++ b/src/main/java/fr/inra/oresing/persistence/JsonRowMapper.java @@ -126,6 +126,10 @@ public class JsonRowMapper<T> implements RowMapper<T> { }); } + public <C> C toObject(String json, Class<C> clazz) throws JsonProcessingException { + return jsonMapper.readValue(json, clazz); + } + @Override public T mapRow(ResultSet rs, int rowNum) throws SQLException { try { diff --git a/src/main/java/fr/inra/oresing/persistence/JsonTableInApplicationSchemaRepositoryTemplate.java b/src/main/java/fr/inra/oresing/persistence/JsonTableInApplicationSchemaRepositoryTemplate.java index ca8ea18b22898b6799bbe041d9c49b5666a913c0..0ebc72380200897cffcb6fef489f777baf5b8817 100644 --- a/src/main/java/fr/inra/oresing/persistence/JsonTableInApplicationSchemaRepositoryTemplate.java +++ b/src/main/java/fr/inra/oresing/persistence/JsonTableInApplicationSchemaRepositoryTemplate.java @@ -10,7 +10,7 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -abstract class JsonTableInApplicationSchemaRepositoryTemplate<T extends OreSiEntity> extends JsonTableRepositoryTemplate<T> { +public abstract class JsonTableInApplicationSchemaRepositoryTemplate<T extends OreSiEntity> extends JsonTableRepositoryTemplate<T> { private final Application application; diff --git a/src/main/java/fr/inra/oresing/persistence/SqlPrimitiveType.java b/src/main/java/fr/inra/oresing/persistence/SqlPrimitiveType.java index 2d8e560094495f73c4341f0447978499e42f1183..cf8191a409251e6c7d08d37fa280c3262dfd2cbd 100644 --- a/src/main/java/fr/inra/oresing/persistence/SqlPrimitiveType.java +++ b/src/main/java/fr/inra/oresing/persistence/SqlPrimitiveType.java @@ -13,7 +13,6 @@ public enum SqlPrimitiveType { NUMERIC, COMPOSITE_DATE, BOOLEAN, - JSONB; /** diff --git a/src/main/java/fr/inra/oresing/persistence/SqlSchemaForRelationalViewsForApplication.java b/src/main/java/fr/inra/oresing/persistence/SqlSchemaForRelationalViewsForApplication.java index 5e3b825dd2abab6f14bcb240b699a29054515ba7..aa5d1666897d7e01b5bc6edbb4f855587baf9893 100644 --- a/src/main/java/fr/inra/oresing/persistence/SqlSchemaForRelationalViewsForApplication.java +++ b/src/main/java/fr/inra/oresing/persistence/SqlSchemaForRelationalViewsForApplication.java @@ -1,10 +1,10 @@ package fr.inra.oresing.persistence; +import com.google.common.base.Preconditions; import fr.inra.oresing.model.Application; import fr.inra.oresing.model.ReferenceColumn; import fr.inra.oresing.rest.ViewStrategy; import lombok.Value; -import org.testcontainers.shaded.com.google.common.base.Preconditions; public record SqlSchemaForRelationalViewsForApplication(Application application, diff --git a/src/main/java/fr/inra/oresing/persistence/SqlService.java b/src/main/java/fr/inra/oresing/persistence/SqlService.java index ba2e01517e65321f2529d68514c59cc887e6d5e5..f214589212aa5471d3cbdde3f31ba21abebea637 100644 --- a/src/main/java/fr/inra/oresing/persistence/SqlService.java +++ b/src/main/java/fr/inra/oresing/persistence/SqlService.java @@ -22,30 +22,37 @@ public class SqlService { private NamedParameterJdbcTemplate namedParameterJdbcTemplate; - public void createSchema(SqlSchema schema, OreSiRole owner) { namedParameterJdbcTemplate.getJdbcTemplate(). - execute("CREATE SCHEMA " + schema.getSqlIdentifier() + " AUTHORIZATION " + owner.getSqlIdentifier()); + execute("CREATE SCHEMA " + schema.getSqlIdentifier() + " AUTHORIZATION " + owner.getSqlIdentifier()); } public void dropSchema(SqlSchemaForRelationalViewsForApplication schema) { - execute("DROP SCHEMA IF EXISTS %s CASCADE".formatted(schema.getSqlIdentifier())); + execute("DROP SCHEMA IF EXISTS %s CASCADE" + .formatted(schema.getSqlIdentifier())); } public void grantUsage(SqlSchema schema, OreSiRole readerOnApplicationRole) { - execute("GRANT USAGE ON SCHEMA " + schema.getSqlIdentifier() + " TO " + readerOnApplicationRole.getSqlIdentifier()); + execute("GRANT USAGE ON SCHEMA %s TO %s" + .formatted(schema.getSqlIdentifier(), readerOnApplicationRole.getSqlIdentifier())); } public void setSchemaOwner(SqlSchema schema, OreSiRole owner) { - execute("ALTER SCHEMA " + schema.getSqlIdentifier() + " OWNER TO " + owner.getSqlIdentifier()); + execute("ALTER SCHEMA %s OWNER TO %s" + .formatted(schema.getSqlIdentifier(), owner.getSqlIdentifier())); } public void createTable(SqlTable table, String contentSql) { - execute("CREATE TABLE " + table.getSqlIdentifier() + " AS (" + contentSql + ")"); + execute("CREATE TABLE %s AS (%s)" + .formatted(table.getSqlIdentifier(), contentSql)); } public void dropTable(SqlTable table) { - execute("DROP TABLE " + table.getSqlIdentifier()); + execute( + "DROP TABLE %s" + .formatted(table.getSqlIdentifier() + ) + ); } public void setTableOwner(SqlTable table, OreSiRole owner) { @@ -53,25 +60,29 @@ public class SqlService { } public void enableRowLevelSecurity(SqlTable table) { - execute("ALTER TABLE " + table.getSqlIdentifier() + " ENABLE ROW LEVEL SECURITY"); + execute("ALTER TABLE %s ENABLE ROW LEVEL SECURITY" + .formatted(table.getSqlIdentifier())); } public void createView(SqlTable view, String viewSql) { - execute("CREATE VIEW " + view.getSqlIdentifier() + " AS (" + - viewSql + ")"); + execute("CREATE VIEW %s AS (%s)" + .formatted(view.getSqlIdentifier(), viewSql)); } public void setViewOwner(SqlTable view, OreSiRightOnApplicationRole owner) { - execute("ALTER VIEW " + view.getSqlIdentifier() + " OWNER TO " + owner.getSqlIdentifier()); + execute("ALTER VIEW %s OWNER TO %s" + .formatted(view.getSqlIdentifier(), owner.getSqlIdentifier())); } public void dropView(SqlTable view) { - execute("DROP VIEW " + view.getSqlIdentifier()); + execute("DROP VIEW %s" + .formatted(view.getSqlIdentifier())); } public List<PolicyDescription> getPoliciesForRole(OreSiRightOnApplicationRole role) { - String sql = "select policyname, schemaname, tablename from pg_policies " + - "where Array[:role::name] @> roles ;"; + String sql = """ + select policyname, schemaname, tablename from pg_policies where Array[:role::name] @> roles ; + """; return namedParameterJdbcTemplate.query(sql, Map.of("role", role.getAsSqlRole()), PolicyDescription::convert); } @@ -79,6 +90,7 @@ public class SqlService { dropPolicy(sqlPolicy); createPolicy(sqlPolicy.policyToCreateSql()); } + public void createPolicy(String createPolicySql) { execute(createPolicySql); } @@ -88,17 +100,22 @@ public class SqlService { } public void createRole(OreSiRoleManagedByApplication roleManagedByApplication) { - String sql = "CREATE ROLE " + roleManagedByApplication.getSqlIdentifier(); + String sql = "CREATE ROLE %s" + .formatted(roleManagedByApplication.getSqlIdentifier()); execute(sql); } public void createRoleWithPublic(OreSiRoleManagedByApplication roleManagedByApplication) { - String sql = "CREATE ROLE " + roleManagedByApplication.getSqlIdentifier() + " in role \""+SqlSchemaForApplication.PUBLIC_UUID.toString()+"\""; + String sql = """ + CREATE ROLE %s in role "%s" + """ + .formatted(roleManagedByApplication.getSqlIdentifier(), SqlSchemaForApplication.PUBLIC_UUID.toString()); execute(sql); } public void dropRole(OreSiRoleManagedByApplication roleManagedByApplication) { - String sql = "DROP ROLE " + roleManagedByApplication.getSqlIdentifier(); + String sql = "DROP ROLE %s" + .formatted(roleManagedByApplication.getSqlIdentifier()); execute(sql); } @@ -118,7 +135,10 @@ public class SqlService { } public void removeUserInRole(OreSiRoleWeCanGrantOtherRolesTo roleToModify, OreSiRoleToBeGranted roleToAdd) { - String sql = "REVOKE " + roleToAdd.getSqlIdentifier() + " FROM " + roleToModify.getSqlIdentifier(); + String sql = "REVOKE %1$s FROM %2$s" + .formatted(roleToAdd.getSqlIdentifier(), + roleToModify.getSqlIdentifier() + ); execute(sql); } @@ -127,12 +147,14 @@ public class SqlService { } public void setRole(OreSiRoleToAccessDatabase roleToAccessDatabase) { - String sql = "SET LOCAL ROLE " + roleToAccessDatabase.getSqlIdentifier(); + String sql = "SET LOCAL ROLE %s" + .formatted(roleToAccessDatabase.getSqlIdentifier()); execute(sql); } public boolean hasRole(OreSiRole role) { - String sql = "SELECT pg_has_role('" + role.getAsSqlRole() + "', 'MEMBER')"; + String sql = "SELECT pg_has_role('%s', 'MEMBER')" + .formatted(role.getAsSqlRole()); boolean hasRole = Boolean.TRUE.equals(namedParameterJdbcTemplate.queryForObject(sql, EmptySqlParameterSource.INSTANCE, Boolean.class)); return hasRole; } diff --git a/src/main/java/fr/inra/oresing/persistence/requestBuilder/datatype/DataTypeRequestBuilder.java b/src/main/java/fr/inra/oresing/persistence/requestBuilder/datatype/DataTypeRequestBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..ebc62bfd1eeef6b792bc56534522e1ff5c4c1429 --- /dev/null +++ b/src/main/java/fr/inra/oresing/persistence/requestBuilder/datatype/DataTypeRequestBuilder.java @@ -0,0 +1,796 @@ +package fr.inra.oresing.persistence.requestBuilder.datatype; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import fr.inra.oresing.checker.CheckerType; +import fr.inra.oresing.checker.Multiplicity; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.model.data.*; +import fr.inra.oresing.persistence.SqlSchema; +import fr.inra.oresing.persistence.SqlSchemaForApplication; +import org.apache.commons.collections4.CollectionUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class DataTypeRequestBuilder { + public static final String DELETE_SEARCH = """ + DELETE + FROM %1$s.data + WHERE + application=:applicationId::uuid + AND data.datatype=:datatype + """; + public static final String SELECT_SEARCH = """ + SELECT + '%1$s' AS "@class", + jsonb_build_object( + 'rowNumber', row_number() over (), + 'totalRows', count(*) over (), + 'rowId', rowId, + 'values', jsonb_object_agg(dataValues %2$s %3$s)%7$s , + 'refsLinkedTo', jsonb_object_agg(refsLinkedTo %4$s %5$s)%8$s + ) AS "json" + FROM %6$s.data + WHERE + application=:applicationId::uuid + AND data.datatype=:datatype + """; + private DownloadDatasetQuery downloadDatasetQuery; + + private SqlSchemaForApplication schema; + private AtomicInteger atomicInteger = new AtomicInteger(); + private MapSqlParameterSource paramSource = new MapSqlParameterSource(); + + sealed interface SelectRequestWhere permits SelectRequestWhereInSelect, SelectRequestWhereHaving { + Supplier<String> filter(); + + default String build() { + if (filter() == null) { + return ""; + } + return switch (this) { + case SelectRequestWhereInSelect selectRequestWhereInWith -> + "\n AND (\n%1$s\n)".formatted(filter().get()); + case SelectRequestWhereHaving selectRequestWhereHavingInWith -> + "\n HAVING (\n%1$s\n)".formatted(filter().get()); + }; + } + + } + + + record SelectRequestWhereInSelect(Supplier<String> filter) implements SelectRequestWhere { + } + + record SelectRequestWhereHaving(Supplier<String> filter) implements SelectRequestWhere { + } + + public DataTypeRequestBuilder(DownloadDatasetQuery downloadDatasetQuery) { + this.downloadDatasetQuery = downloadDatasetQuery; + this.schema = SqlSchema.forApplication(downloadDatasetQuery.application()); + } + + static String sanitize(String key) { + return Optional.ofNullable(key) + .map(s -> s.replaceAll("'", "''")) + .orElse(null); + } + + public static SqlRequest buildSelectRequest(DownloadDatasetQuery downloadDatasetQuery) { + DataTypeRequestBuilder dataTypeRequestBuilder = new DataTypeRequestBuilder(downloadDatasetQuery); + return dataTypeRequestBuilder.buildRequestSelect(); + } + + private static String variableComponentToSqlPath(String variable, String component) { + return List.of(sanitize(variable), sanitize(component)).stream().collect(Collectors.joining(",")); + } + + public static SqlRequest buildDeleteRequest(DownloadDatasetQuery downloadDatasetQuery) { + DataTypeRequestBuilder dataTypeRequestBuilder = new DataTypeRequestBuilder(downloadDatasetQuery); + return dataTypeRequestBuilder.buildRequestDelete(); + } + + private String addArgumentAndReturnSubstitution(Object value) { + int i = this.atomicInteger.incrementAndGet(); + String paramName = String.format("arg%d", i); + paramSource.addValue(paramName, value); + return String.format(":%s", paramName); + } + + private SqlRequest buildRequestDelete() { + this.paramSource = new MapSqlParameterSource("applicationId", downloadDatasetQuery.application().getId()); + + return switch (downloadDatasetQuery) { + case DownloadDatasetQueryNoFilter downloadDatasetQueryNoFilter -> + buildDeleteDatasetQuery(downloadDatasetQueryNoFilter, null, null); + case DownloadDatasetQueryAdvancedSearch downloadDatasetQueryAdvancedSearch -> buildDeleteDatasetQuery( + downloadDatasetQuery, + null, + new SelectRequestWhereHaving(() -> + buildDownloadDatasetFilterAdvancedSearch(downloadDatasetQueryAdvancedSearch) + ) + ); + case DownloadDatasetQueryByRowId downloadDatasetQueryByRowId -> { + paramSource.addValue("rowids", downloadDatasetQueryByRowId.rowIds().stream() + .map(DownloadDatasetQueryByRowId.DataRowIds::id).toList()); + String filter = """ + \s(data.rowid::uuid IN (:rowids)) + """; + yield buildDeleteDatasetQuery( + downloadDatasetQueryByRowId, + new SelectRequestWhereInSelect(() -> filter), + null + ); + } + case DownloadDatasetQuerySimpleSearch downloadDatasetQuerySimpleSearch -> { + yield buildDeleteDatasetQuery( + downloadDatasetQuerySimpleSearch, + new SelectRequestWhereInSelect(() -> + buildFilter( + downloadDatasetQuerySimpleSearch.authorizationDescriptions() + ) + ), + null + ); + } + }; + + } + + private SqlRequest buildRequestSelect() { + this.paramSource = new MapSqlParameterSource("applicationId", downloadDatasetQuery.application().getId()); + paramSource.addValue("datatype", downloadDatasetQuery.dataType()); + return getSqlRequest(); + } + + @NotNull + private SqlRequest getSqlRequest() { + return switch (downloadDatasetQuery) { + case DownloadDatasetQueryNoFilter downloadDatasetQueryNoFilter -> + buildDownloadDatasetQuery(downloadDatasetQueryNoFilter, null, null); + case DownloadDatasetQueryAdvancedSearch downloadDatasetQueryAdvancedSearch -> buildDownloadDatasetQuery( + downloadDatasetQuery, + null, + new SelectRequestWhereHaving(() -> + buildDownloadDatasetFilterAdvancedSearch(downloadDatasetQueryAdvancedSearch) + ) + ); + case DownloadDatasetQueryByRowId downloadDatasetQueryByRowId -> { + paramSource.addValue("rowids", downloadDatasetQueryByRowId.rowIds().stream() + .map(DownloadDatasetQueryByRowId.DataRowIds::id).toList()); + String filter = """ + \s(data.rowid::uuid IN (:rowids)) + """; + yield buildDownloadDatasetQuery( + downloadDatasetQueryByRowId, + new SelectRequestWhereInSelect(() -> filter), + null + ); + } + case DownloadDatasetQuerySimpleSearch downloadDatasetQuerySimpleSearch -> { + yield buildDownloadDatasetQuery( + downloadDatasetQuerySimpleSearch, + new SelectRequestWhereInSelect(() -> + buildFilter( + downloadDatasetQuerySimpleSearch.authorizationDescriptions() + ) + ), + null + ); + } + }; + } + + private SqlRequest buildDeleteDatasetQuery(DownloadDatasetQuery downloadDatasetQuery, + SelectRequestWhereInSelect filterInSelect, + SelectRequestWhereHaving filterInHaving) { + return new DeleteRequest( + downloadDatasetQuery, + paramSource, + new DeleteRequest.DeleteRequestRequest( + schema.getSqlIdentifier(), + filterInSelect, + filterInHaving + ) + ).build(); + } + + private SqlRequest buildDownloadDatasetQuery(DownloadDatasetQuery downloadDatasetQuery, + SelectRequestWhereInSelect filterInSelect, + SelectRequestWhereHaving filterInHaving) { + return new SelectRequest( + downloadDatasetQuery, + paramSource, + new SelectRequest.SelectRequestRequest( + buildRemoveSqlSelectNotInValues(downloadDatasetQuery.variableComponentSelects()), + schema.getSqlIdentifier(), + filterInSelect, + filterInHaving + ), + new SelectRequest.SelectRequestOrderBy(downloadDatasetQuery.variableComponentOrderBy()), + new SelectRequest.SelectRequestOffset(downloadDatasetQuery.outPut()), + new SelectRequest.SelectRequestLimit(downloadDatasetQuery.outPut()) + ).build(); + } + + private List<BuildRemoveSqlSelectNotInValues> buildRemoveSqlSelectNotInValues(Set<VariableComponentKey> select) { + if (CollectionUtils.isEmpty(select)) { + return List.of(); + } + record SelectedComponent(String variable, String component, boolean isSelected, boolean isReference, + boolean isVariableHidden, + + boolean isHidden) { + static String valuesPathToHide = """ + #- '{%s}'::text[] + """; + static String refsLinkedToPathToHide = """ + #- '{%s}'::text[] + """; + + public BuildRemoveSqlSelectNotInValues valuePathToHide() { + return new BuildRemoveSqlSelectNotInValues( + valuesPathToHide.formatted(variableComponentToSqlPath(variable(), component())), + isReference ? refsLinkedToPathToHide.formatted(variableComponentToSqlPath(variable(), component())) : null + ); + } + } + record SelectedVariable(String variable, List<SelectedComponent> selectedComponents, boolean isHidden) { + static String valuesPathToHide = """ + #- '{%s}'::text[] + """; + static String refsLinkedToPathToHide = """ + #- '{%s}'::text[] + """; + + public List<BuildRemoveSqlSelectNotInValues> buildRemoveSqlSelectNotInValues() { + if (isHidden() || selectedComponents().stream().noneMatch(selectedComponent -> selectedComponent.isSelected())) { + boolean isReference = selectedComponents().stream().anyMatch(selectedComponent -> selectedComponent.isReference()); + return List.of(new BuildRemoveSqlSelectNotInValues( + valuesPathToHide.formatted(sanitize(variable)), + isReference ? refsLinkedToPathToHide.formatted(sanitize(variable)) : null + ) + ); + } else { + return selectedComponents().stream() + .filter(selectedComponent -> selectedComponent.isHidden || !selectedComponent.isSelected) + .map(SelectedComponent::valuePathToHide) + .collect(Collectors.toList()); + } + } + } + + return downloadDatasetQuery.getDatatypeConfiguration().doGetAllVariableComponents() + .stream() + .map(variableComponentKey -> new SelectedComponent( + variableComponentKey.variable(), + variableComponentKey.component(), + select.contains(variableComponentKey), + Optional.ofNullable(downloadDatasetQuery) + .map(DownloadDatasetQuery::getDatatypeConfiguration) + .map(Configuration.DataTypeDescription::getData) + .map(map -> map.get(variableComponentKey.variable())) + .map(Configuration.VariableDescription::getComponents) + .map(map -> map.get(variableComponentKey.component())) + .map(Configuration.VariableComponentDescription::getChecker) + .map(Configuration.CheckerDescription::getName) + .map(checkerType -> CheckerType.Reference.equals(checkerType)) + .orElse(false), + Optional.ofNullable(downloadDatasetQuery) + .map(DownloadDatasetQuery::getDatatypeConfiguration) + .map(Configuration.DataTypeDescription::getData) + .map(map -> map.get(variableComponentKey.variable())) + .map(Configuration.VariableDescription::isHidden) + .orElse(false), + Optional.ofNullable(downloadDatasetQuery) + .map(DownloadDatasetQuery::getDatatypeConfiguration) + .map(Configuration.DataTypeDescription::getData) + .map(map -> map.get(variableComponentKey.variable())) + .map(Configuration.VariableDescription::getComponents) + .map(map -> map.get(variableComponentKey.component())) + .map(Configuration.VariableComponentDescription::isHidden) + .orElse(false) + ) + ) + .collect(Collectors.groupingBy(SelectedComponent::variable)) + .entrySet().stream() + .map(e -> new SelectedVariable(e.getKey(), e.getValue(), e.getValue().stream().anyMatch(SelectedComponent::isVariableHidden))) + .map(SelectedVariable::buildRemoveSqlSelectNotInValues) + .flatMap(List::stream) + .distinct() + .collect(Collectors.toList()); + } + + private String buildDownloadDatasetFilterAdvancedSearch(DownloadDatasetQueryAdvancedSearch + downloadDatasetQueryAdvancedSearch) { + return downloadDatasetQueryAdvancedSearch.variableComponentFilters().stream() + .map(this::filter) + .filter(f -> !Strings.isNullOrEmpty(f)) + .collect(Collectors.joining(" ) AND \n( ", "( ", " )\n")); + } + + public String filter(DownloadDatasetQueryAdvancedSearch.VariableComponentFilters variableComponentFilters) { + return switch (variableComponentFilters) { + case DownloadDatasetQueryAdvancedSearch.NoVariableComponentFilters noVariableComponentFilters -> null; + case DownloadDatasetQueryAdvancedSearch.VariableComponentFilterForInterval variableComponentFilterForInterval -> { + yield switch (variableComponentFilterForInterval) { + + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByNumeric( + VariableComponentKey(String variable, String component), + DownloadDatasetQueryAdvancedSearch.IntervalValuesNumeric intervalValues, + Multiplicity multiplicity + ) -> switch (multiplicity) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@ >= $from && @ <= $to))', '{"from":%3$s, "to":%4$s}') + """ + .formatted( + sanitize(variable), + sanitize(component), + intervalValues.fromFromNumeric(), + intervalValues.fromToNumeric() + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@ >= $from && @ <= $to))', '{"from":%3$s, "to":%4$s}') + """ + .formatted( + sanitize(variable), + sanitize(component), + intervalValues.fromFromNumeric(), + intervalValues.fromToNumeric() + ); + }; + + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByTemporal variableComponentFiltersForIntervalByTemporal -> + switch (variableComponentFiltersForIntervalByTemporal.multiplicity()) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")>="%4$s".datetime() && @.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")<="%5$s".datetime() ))') + """.formatted( + sanitize(variableComponentFiltersForIntervalByTemporal.variableComponentKey().variable()), + sanitize(variableComponentFiltersForIntervalByTemporal.variableComponentKey().component()), + variableComponentFiltersForIntervalByTemporal.intervalValues().format(), + variableComponentFiltersForIntervalByTemporal.intervalValues().getFromIsoString(), + variableComponentFiltersForIntervalByTemporal.intervalValues().getToIsoString() + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")>="%4$s".datetime() && @.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")<="%5$s".datetime() ))') + """.formatted( + sanitize(variableComponentFiltersForIntervalByTemporal.variableComponentKey().variable()), + sanitize(variableComponentFiltersForIntervalByTemporal.variableComponentKey().component()), + variableComponentFiltersForIntervalByTemporal.intervalValues().format(), + variableComponentFiltersForIntervalByTemporal.intervalValues().getFromIsoString(), + variableComponentFiltersForIntervalByTemporal.intervalValues().getToIsoString() + ); + }; + }; + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFilterSimpleSearch variableComponentFilterSimpleSearch -> + switch (variableComponentFilterSimpleSearch) { + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByNumeric( + VariableComponentKey(String variable, String component), + String filter, + Multiplicity multiplicity + ) -> switch (multiplicity) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@ == $filter))', '{"filter":%3$s}') + """.formatted( + sanitize(variable), + sanitize(component), + DownloadDatasetQueryAdvancedSearch.FieldType.convertToNumber(filter) + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@ == $filter))', '{"filter":%3$s}') + """.formatted( + sanitize(variable), + sanitize(component), + DownloadDatasetQueryAdvancedSearch.FieldType.convertToNumber(filter) + ); + }; + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByReference( + VariableComponentKey(String variable, String component), + String filter, + Multiplicity multiplicity + ) -> switch (multiplicity) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@ == $filterEquals || @ starts with $filterStarts))', '{"filterEquals":"%3$s","filterStarts":"%4$s"}') + """.formatted( + sanitize(variable), + sanitize(component), + filter, + filter + "." + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@ == $filterEquals || @ starts with $filterStarts))', '{"filterEquals":"%3$s","filterStarts":"%4$s"}') + """.formatted( + sanitize(variable), + sanitize(component), + filter, + filter + "." + ); + }; + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByPlainText( + VariableComponentKey(String variable, String component), + String filter, + Multiplicity multiplicity + ) -> switch (multiplicity) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@ like_regex "%3$s" flag "q"))') + """.formatted( + sanitize(variable), + sanitize(component), + sanitize(filter.replaceAll("(\\.)", "\\\\$1")) + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@ like_regex "%3$s" flag "q"))') + """.formatted( + sanitize(variable), + sanitize(component), + sanitize(filter.replaceAll("(\\.)", "\\\\$1")) + ); + }; + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByRegexp( + VariableComponentKey(String variable, String component), + String filter, + Multiplicity multiplicity + ) -> switch (multiplicity) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@ like_regex "%3$s" ))') + """.formatted( + sanitize(variable), + sanitize(component), + sanitize(filter) + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@ like_regex "%3$s"))') + """.formatted( + sanitize(variable), + sanitize(component), + sanitize(filter) + ); + }; + case DownloadDatasetQueryAdvancedSearch.WithFormatForFilterDate withFormatForFilterDate -> + switch (variableComponentFilters.multiplicity()) { + case ONE -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($ ? (@.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")=="%4$s".datetime()))') + """.formatted( + sanitize(withFormatForFilterDate.variableComponentKey().variable()), + sanitize(withFormatForFilterDate.variableComponentKey().component()), + withFormatForFilterDate.format(), + withFormatForFilterDate.getIsoString() + ); + case MANY -> """ + jsonb_path_match(jsonb_object_agg(dataValues #> '{%1$s,%2$s}'), 'exists($[*] ? (@.datetime("\\"date:\\"YYYY-MM-DD\\"T\\"HH24:MI:SS\\":%3$s\\"")=="%4$s".datetime()))') + """.formatted( + sanitize(withFormatForFilterDate.variableComponentKey().variable()), + sanitize(withFormatForFilterDate.variableComponentKey().component()), + withFormatForFilterDate.format(), + withFormatForFilterDate.getIsoString() + ); + }; + }; + }; + } + + + private String buildFilter(Set<DownloadDatasetQuerySimpleSearch.AuthorizationDescription> authorizationDescriptions) { + String sqlStart = "data.authorization @> "; + String sqlEnd = ":: %s.authorization[]".formatted(schema.getSqlIdentifier()); + final String sql = authorizationDescriptions.stream() + .map(authorizationdescription -> authorizationdescription.toAuthorization(downloadDatasetQuery.application(), schema)) + .filter(authorizations -> !authorizations.isEmpty()) + .map(authorizations -> { + String authorizationsSql = authorizations.stream() + .map(authorization -> authorization.toSQL(downloadDatasetQuery.application().getConfiguration().getRequiredAuthorizationsAttributes())) + .map(expression -> String.format(expression, schema.getSqlIdentifier())) + .collect(Collectors.joining(",", "ARRAY[\n\t\t\t\t", "\n\t\t\t]")); + return new StringBuilder(sqlStart) + .append(authorizationsSql) + .append(sqlEnd); + }) + .collect(Collectors.joining(") \n\t\tOR\n\t\t\t(", "\n\t\t\t(", ")")); + return " (%s)".formatted(sql); + } + + record DeleteRequest( + DownloadDatasetQuery downloadDatasetQuery, + MapSqlParameterSource parameterSource, + DeleteRequest.DeleteRequestRequest deleteRequestRequest + ) { + SqlRequest build() { + parameterSource().addValue("application", downloadDatasetQuery().application().getId()); + parameterSource().addValue("datatype", downloadDatasetQuery().dataType()); + String select = Optional.ofNullable(deleteRequestRequest()).map(DeleteRequest.DeleteRequestRequest::build).orElse(""); + Joiner.on("\n") + .join( + List.of( + Optional.ofNullable(deleteRequestRequest()).map(DeleteRequest.DeleteRequestRequest::build).orElse("") + ) + ); + return new SqlRequest( + select, + parameterSource() + ); + } + + record DeleteRequestRequest( + String from, + SelectRequestWhereInSelect requestWhereInSelect, + SelectRequestWhereHaving filterInHaving) { + static String TEMPLATE = """ + DELETE + FROM %1$s."data" del + WHERE rowid in ( + SELECT + rowId + FROM %1$s."data" + WHERE + application=:applicationId::uuid + AND data.datatype=:datatype%2$s + GROUP BY data.rowid + %3$s + ) + RETURNING rowid::UUID; + """; + + public String build() { + return TEMPLATE.formatted( + from, + Optional.ofNullable(requestWhereInSelect()).map(SelectRequestWhere::build).orElse(""), + Optional.ofNullable(filterInHaving()) + .map(SelectRequestWhere::build) + .orElse("") + ); + } + } + + record SelectRequestSelect(String from) { + static String TEMPLATE = """ + SELECT * + FROM %1$s + """; + + String build() { + return TEMPLATE + .formatted( + from() + ); + } + } + + record SelectRequestOffset(DownloadDatasetQuery.OutPut outPut) { + public String build() { + + return (outPut().offset() != null && outPut().offset() >= 0) ? + """ + OFFSET %d ROWS + """.formatted(outPut().offset()) : + ""; + } + } + + record SelectRequestLimit(DownloadDatasetQuery.OutPut outPut) { + public String build() { + + return (outPut().limit() != null && outPut().limit() >= 0) ? + """ + OFFSET %d ROWS + """.formatted(outPut().limit()) : + ""; + } + } + + record SelectRequestOrderBy(Set<DownloadDatasetQuery.VariableComponentOrderBy> variableComponentOrderBy) { + public String build() { + return (CollectionUtils.isNotEmpty(variableComponentOrderBy())) ? + variableComponentOrderBy().stream() + .map(vckob -> { + String cast = switch (vckob.sqlType()) { + case CheckerType.VariableComponentType.VariableComponentBooleanType variableComponentBooleanType -> + "BOOL"; + case CheckerType.VariableComponentType.VariableComponentDateType variableComponentDateType -> + "COMPOSITE_DATE::TIMESTAMP"; + case CheckerType.VariableComponentType.VariableComponentNumericType variableComponentNumericType -> + "NUMERIC"; + case CheckerType.VariableComponentType.VariableComponentReferenceType variableComponentReferenceType -> + "LTREE"; + case CheckerType.VariableComponentType.VariableComponentTextType variableComponentTextType -> + "TEXT"; + }; + return """ + (json #>>'{%1$s}')::%2$s """ + .formatted( + variableComponentToSqlPath(vckob.variableComponentKey().variable(), vckob.variableComponentKey().component()), + cast + ); + } + ) + .collect(Collectors.joining( + ",\n") + ) : + ""; + } + + } + + } + + record SelectRequest( + DownloadDatasetQuery downloadDatasetQuery, + MapSqlParameterSource parameterSource, + SelectRequestRequest selectRequestRequest, + SelectRequestOrderBy orderBy, + SelectRequestOffset offset, + SelectRequestLimit limit + ) { + SqlRequest build() { + parameterSource().addValue("application", downloadDatasetQuery().application().getId()); + parameterSource().addValue("datatype", downloadDatasetQuery().dataType()); + String select = Optional.ofNullable(selectRequestRequest()).map(SelectRequestRequest::build).orElse(""); + Joiner.on("\n") + .join( + List.of( + Optional.ofNullable(selectRequestRequest()).map(SelectRequestRequest::build).orElse("") + ) + ); + if (!CollectionUtils.isEmpty(orderBy().variableComponentOrderBy())) { + select = """ + WITH t as ( + %1$s + ) + SELECT * + FROM t + ORDER BY + %2$s + %3$s + %4$s + """.formatted( + select, + Optional.ofNullable(orderBy()).map(SelectRequestOrderBy::build).orElse(""), + Optional.ofNullable(offset()).map(SelectRequestOffset::build).orElse(""), + Optional.ofNullable(limit()).map(SelectRequestLimit::build).orElse("") + ); + } else { + select = Joiner.on("\n") + .join( + select, + Optional.ofNullable(offset()).map(SelectRequestOffset::build).orElse(""), + Optional.ofNullable(limit()).map(SelectRequestLimit::build).orElse("") + ); + } + return new SqlRequest( + select, + parameterSource() + ); + } + + record SelectRequestRequest( + List<BuildRemoveSqlSelectNotInValues> buildRemoveSqlSelectNotInValues, + String from, + SelectRequestWhereInSelect requestWhereInSelect, + SelectRequestWhereHaving filterInHaving) { + static String TEMPLATE = """ + SELECT + 'fr.inra.oresing.persistence.DataRow' AS "@class", + jsonb_build_object( + 'rowNumber', row_number() over (), + 'totalRows', count(*) over (), + 'rowId', rowId, + 'values', jsonb_object_agg(dataValues \s + %1$s\s + ) , + 'refsLinkedTo', jsonb_object_agg(refsLinkedTo \s + %2$s\s + ) + ) AS "json" + FROM %3$s."data" + WHERE + application=:applicationId::uuid + AND data.datatype=:datatype%4$s + GROUP BY data.rowid + %5$s + """; + + public String build() { + return TEMPLATE.formatted( + buildRemoveSqlSelectNotInValues.stream() + .map(BuildRemoveSqlSelectNotInValues::valuePathToHide) + .distinct() + .filter(Objects::nonNull) + .collect(Collectors.joining("")), + buildRemoveSqlSelectNotInValues.stream() + .map(BuildRemoveSqlSelectNotInValues::refsLinkedToPathToHide) + .distinct() + .filter(Objects::nonNull) + .collect(Collectors.joining("")), + from, + Optional.ofNullable(requestWhereInSelect()).map(SelectRequestWhere::build).orElse(""), + Optional.ofNullable(filterInHaving()) + .map(SelectRequestWhere::build) + .orElse("") + ); + } + } + + record SelectRequestSelect(String from) { + static String TEMPLATE = """ + SELECT * + FROM %1$s + """; + + String build() { + return TEMPLATE + .formatted( + from() + ); + } + } + + record SelectRequestOffset(DownloadDatasetQuery.OutPut outPut) { + public String build() { + + return (outPut().offset() != null && outPut().offset() >= 0) ? + """ + OFFSET %d ROWS + """.formatted(outPut().offset()) : + ""; + } + } + + record SelectRequestLimit(DownloadDatasetQuery.OutPut outPut) { + public String build() { + + return (outPut().limit() != null && outPut().limit() >= 0) ? + """ + LIMIT %d + """.formatted(outPut().limit()) : + ""; + } + } + + record SelectRequestOrderBy(Set<DownloadDatasetQuery.VariableComponentOrderBy> variableComponentOrderBy) { + public String build() { + return (CollectionUtils.isNotEmpty(variableComponentOrderBy())) ? + variableComponentOrderBy().stream() + .map(vckob -> { + String cast = switch (vckob.sqlType()) { + case CheckerType.VariableComponentType.VariableComponentBooleanType variableComponentBooleanType -> + "BOOL"; + case CheckerType.VariableComponentType.VariableComponentDateType variableComponentDateType -> + "COMPOSITE_DATE::TIMESTAMP"; + case CheckerType.VariableComponentType.VariableComponentNumericType variableComponentNumericType -> + "NUMERIC"; + case CheckerType.VariableComponentType.VariableComponentReferenceType variableComponentReferenceType -> + "LTREE"; + case CheckerType.VariableComponentType.VariableComponentTextType variableComponentTextType -> + "TEXT"; + }; + return """ + (json #>>'{%1$s}')::%2$s """ + .formatted( + variableComponentToSqlPath(vckob.variableComponentKey().variable(), vckob.variableComponentKey().component()), + cast + ); + } + ) + .collect(Collectors.joining( + ",\n") + ) : + ""; + } + + } + + } + + public record SqlRequest(String sql, MapSqlParameterSource parameterSource) { + } + + record BuildRemoveSqlSelectNotInValues(String valuePathToHide, String refsLinkedToPathToHide) { + } +} + diff --git a/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java b/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java index 1b94b12ba51932d826b354f0b4290235bbe604e9..1d72f6b11b2d2e1167179f54a14d4913658fdb45 100644 --- a/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java +++ b/src/main/java/fr/inra/oresing/persistence/roles/OreSiRole.java @@ -31,6 +31,14 @@ OreSiRole extends WithSqlIdentifier { default String addUserInRoleSql(OreSiRoleWeCanGrantOtherRolesTo roleToModify, boolean withAdminOption) { String withAdminOptionClause = withAdminOption ? " WITH ADMIN OPTION" : ""; - return "GRANT " + getSqlIdentifier() + " TO " + roleToModify.getSqlIdentifier() + withAdminOptionClause; + return """ + GRANT %1$s TO %2$s%3$s; + GRANT %1$s TO superadmin WITH ADMIN TRUE; + """ + .formatted( + getSqlIdentifier(), + roleToModify.getSqlIdentifier(), + withAdminOptionClause + ); } } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java b/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java index 4131c0fe377dca8199c0d0b1d8f7a68d37708791..5831dde3e8f9f5092f2769b1e6f06bd2edb6781b 100644 --- a/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java +++ b/src/main/java/fr/inra/oresing/rest/ApplicationConfigurationService.java @@ -182,22 +182,22 @@ public class ApplicationConfigurationService { verifyLabelNamesForReferences(progressionValidation, builder, app, referenceEntry); } - for (Map.Entry<String, Configuration.DataTypeDescription> datataypeEntry : configuration.getDataTypes().entrySet()) { + for (Map.Entry<String, Configuration.DataTypeDescription> dataTypeEntry : configuration.getDataTypes().entrySet()) { progressionValidation.pushMessage("checkingDatatype", Map.of( "application", configuration.getApplication().getName(), "compositeReference", - datataypeEntry.getKey())); + dataTypeEntry.getKey())); progressionValidation = progressionValidationConficuration.incrementAndPush(progressionValidation); - String dataType = datataypeEntry.getKey(); - Configuration.DataTypeDescription dataTypeDescription = datataypeEntry.getValue(); + String dataType = dataTypeEntry.getKey(); + Configuration.DataTypeDescription dataTypeDescription = dataTypeEntry.getValue(); verifyDataTypeVariableComponentDeclarations(progressionValidation, builder, references, dataType, dataTypeDescription, configuration); verifyDataTypeValidationRules(progressionValidation, builder, dataType, dataTypeDescription, references); verifyInternationalizedColumnsExistsForPatternInDatatype(progressionValidation, configuration, builder, dataType); verifyUniquenessComponentKeysInDatatype(progressionValidation, dataType, dataTypeDescription, builder); verifyDatatypeRepository(progressionValidation, dataType, dataTypeDescription, builder); - verifytagsDefined(progressionValidation, builder, datataypeEntry.getKey(), null, null, dataTypeDescription.getTags(), configuration.tags, "missingDataTypeTagDeclaration"); + verifytagsDefined(progressionValidation, builder, dataTypeEntry.getKey(), null, null, dataTypeDescription.getTags(), configuration.tags, "missingDataTypeTagDeclaration"); Configuration.AuthorizationDescription authorization = dataTypeDescription.getAuthorization(); Set<String> variables = dataTypeDescription.getData().keySet(); @@ -217,7 +217,7 @@ public class ApplicationConfigurationService { verifyDatatypeBindingToExistingVariableComponent(progressionValidation, builder, dataType, variables, variableOccurrencesInDataGroups); verifyDatatypeBindingToExistingVariableComponent(progressionValidation, builder, dataTypeDescription, dataType, variables); verifyChartDescription(progressionValidation, builder, dataType, dataTypeDescription); - verifyLabelNamesForDatatypes(progressionValidation, builder, app, datataypeEntry); + verifyLabelNamesForDatatypes(progressionValidation, builder, app, dataTypeEntry); } } configuration.setRequiredAuthorizationsAttributes(List.copyOf(requiredAuthorizationsAttributesBuilder.build())); diff --git a/src/main/java/fr/inra/oresing/rest/AuthenticationResources.java b/src/main/java/fr/inra/oresing/rest/AuthenticationResources.java index 2d7b5c33f40130cd53cb9f9fcce2875581a06da9..4dff2f2e56b4219d471d595d4e038fda1942e5e6 100644 --- a/src/main/java/fr/inra/oresing/rest/AuthenticationResources.java +++ b/src/main/java/fr/inra/oresing/rest/AuthenticationResources.java @@ -9,6 +9,7 @@ import fr.inra.oresing.persistence.roles.OreSiUserRole; import fr.inra.oresing.rest.model.authorization.LoginResult; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -61,7 +62,12 @@ public class AuthenticationResources { try { authenticationService.sendEmailValidation(login, password); } catch (AuthenticationFailure | NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new RuntimeException("Error sending validation update"); + switch (LocaleContextHolder.getLocale().getLanguage()) { + case "fr"-> throw new RuntimeException("Erreur lors de l'envoi de la mise à jour de validation"); + case "en"-> throw new RuntimeException("Error sending validation update"); + case null, default -> throw new RuntimeException("Error sending validation update"); + } + } String uri = UriUtils.encodePath("/users/" + createUserResult.getUserId().toString(), Charset.defaultCharset()); return ResponseEntity.created(URI.create(uri)).body(Map.of("id", createUserResult.getUserId())); diff --git a/src/main/java/fr/inra/oresing/rest/DataCsvBuilder.java b/src/main/java/fr/inra/oresing/rest/DataCsvBuilder.java index 7a9a2a0fbaf728489eefae9f43b6fa0dfec869c8..6ab89e7e2ae1c3c3a72f7351c7a2208d6830c7a4 100644 --- a/src/main/java/fr/inra/oresing/rest/DataCsvBuilder.java +++ b/src/main/java/fr/inra/oresing/rest/DataCsvBuilder.java @@ -7,7 +7,6 @@ import fr.inra.oresing.checker.CheckerFactory; import fr.inra.oresing.checker.type.DateType; import fr.inra.oresing.checker.type.FieldType; import fr.inra.oresing.checker.type.StringType; -import fr.inra.oresing.model.Application; import fr.inra.oresing.model.Configuration; import fr.inra.oresing.model.ReferenceValue; import fr.inra.oresing.model.VariableComponentKey; @@ -15,14 +14,19 @@ import fr.inra.oresing.persistence.AdditionalFileSearchHelper; import fr.inra.oresing.persistence.DataRepository; import fr.inra.oresing.persistence.DataRow; import fr.inra.oresing.persistence.OreSiRepository; -import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; +import fr.inra.oresing.model.data.DownloadDatasetQuery; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; import lombok.Getter; import org.apache.commons.csv.CSVFormat; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.util.CollectionUtils; +import reactor.core.publisher.Flux; import java.io.IOException; import java.io.OutputStreamWriter; +import java.io.StringWriter; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -30,14 +34,12 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class DataCsvBuilder { - private String dataType; - private Application application; private DownloadDatasetQuery downloadDatasetQuery; private CheckerFactory checkerFactory; private OreSiRepository repo; private ReferenceService referenceService; - private Stream<DataRow> datas; + private Flux<DataRow> datas; private ZipOutputStream zipOutputStream; public static DataCsvBuilder getDataCsvBuilder() { @@ -49,16 +51,6 @@ public class DataCsvBuilder { return this; } - public DataCsvBuilder withApplication(Application application) { - this.application = application; - return this; - } - - public DataCsvBuilder withDatatype(String dataType) { - this.dataType = dataType; - return this; - } - public DataCsvBuilder withCheckerFactory(CheckerFactory checkerFactory) { this.checkerFactory = checkerFactory; return this; @@ -74,52 +66,69 @@ public class DataCsvBuilder { return this; } - public DataCsvBuilder addDatas(Stream<DataRow> datas) { + public DataCsvBuilder addDatas(Flux<DataRow> datas) { this.datas = datas; return this; } public void build() throws IOException { - Configuration.FormatDescription format = downloadDatasetQuery.getApplication() + Configuration.DataTypeDescription dataTypeDescription = downloadDatasetQuery.application() .getConfiguration() .getDataTypes() - .get(dataType) + .get(downloadDatasetQuery.dataType()); + Configuration.FormatDescription format = dataTypeDescription .getFormat(); CSVFormat csvFormat = CSVFormat.EXCEL .withDelimiter(format.getSeparator()) .withSkipHeaderRecord(); CSVWriter writer = new CSVWriter(new OutputStreamWriter(zipOutputStream)); // There is no need for staging the CSV on filesystem or reading bytes into memory. Directly write bytes to the output stream. - ZipEntry zipEntry = new ZipEntry(String.format("%s.csv", dataType)); + ZipEntry zipEntry = new ZipEntry(String.format("%s.csv", downloadDatasetQuery.dataType())); zipOutputStream.putNextEntry(zipEntry); - ImmutableMap<String, DownloadDatasetQuery.VariableComponentOrderBy> allColumns = ImmutableMap.copyOf(getExportColumns(format).entrySet().stream() - .collect(Collectors.toMap( - e -> e.getKey(), - e -> new DownloadDatasetQuery.VariableComponentOrderBy(e.getValue(), DataRepository.Order.ASC) - ))); - ImmutableMap<String, DownloadDatasetQuery.VariableComponentOrderBy> columns; - if (CollectionUtils.isEmpty(downloadDatasetQuery.getVariableComponentOrderBy())) { - columns = allColumns; - } else { - columns = ImmutableMap.copyOf(downloadDatasetQuery.getVariableComponentOrderBy().stream() - .collect(Collectors.toMap(DownloadDatasetQuery.VariableComponentOrderBy::getId, k -> k))); - } - writer.writeNext(columns.keySet().toArray(new String[]{})); + UUIDsfromData uuiDsfromData; AtomicLong counter = new AtomicLong(); - UUIDsfromData uuiDsfromData = new UUIDsfromData(); - datas - .forEach(dataRow -> { - try { - addDatasToZip(dataRow, counter, writer, uuiDsfromData, columns); - } catch (IOException e) { - throw new RuntimeException("can't flush", e); - } - }); - writer.flush(); + try { + ImmutableMap<String, DownloadDatasetQuery.VariableComponentOrderBy> allColumns = ImmutableMap.copyOf(getExportColumns(format, downloadDatasetQuery.variableComponentSelects()).entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey(), + e -> new DownloadDatasetQuery.VariableComponentOrderBy(e.getValue(), DataRepository.Order.ASC, dataTypeDescription.getTypeForVariableComponentKey(e.getValue())) + ))); + ImmutableMap<String, DownloadDatasetQuery.VariableComponentOrderBy> columns; + if (CollectionUtils.isEmpty(downloadDatasetQuery.variableComponentOrderBy())) { + columns = allColumns; + } else { + columns = ImmutableMap.copyOf(downloadDatasetQuery.variableComponentOrderBy().stream() + .collect(Collectors.toMap(o ->o.variableComponentKey().getId(), k -> k))); + } + writer.writeNext(columns.keySet().toArray(new String[]{})); + uuiDsfromData = new UUIDsfromData(); + datas + .subscribe(dataRow -> { + try { + addDatasToZip(dataRow, counter, writer, uuiDsfromData, columns); + } catch (IOException e) { + throw new RuntimeException("can't flush", e); + } + }); + writer.flush(); + } catch (Exception e) { + zipOutputStream.closeEntry(); + zipEntry = new ZipEntry(e.getClass().getSimpleName()); + zipOutputStream.putNextEntry(zipEntry); + switch (LocaleContextHolder.getLocale().getLanguage()) { + case "fr"-> zipOutputStream.write("Une erreur c'est produite lors du télécharger.".getBytes()); + case "en"-> zipOutputStream.write("An error occurred during download.".getBytes()); + case null, default -> zipOutputStream.write("An error occurred during download.".getBytes()); + } + zipOutputStream.write("\n\n".getBytes()); + zipOutputStream.write(e.getMessage().getBytes()); + zipOutputStream.closeEntry(); + return; + } zipOutputStream.closeEntry(); - Stream.of(application.getConfiguration().getReferences()) + Stream.of(downloadDatasetQuery.application().getConfiguration().getReferences()) .filter(a -> !a.isEmpty()) - .forEach(a -> repo.getRepository(application.getId()).referenceValue().getLinkedReferenceValuesStream(uuiDsfromData.referencedUUID) + .forEach(a -> repo.getRepository(downloadDatasetQuery.application().getId()).referenceValue().getLinkedReferenceValuesStream(uuiDsfromData.referencedUUID) .sorted((ref1, ref2) -> ref1.getReferenceType().compareTo(ref2.getReferenceType())) .forEach(referenceValue -> addReferenceEntry(counter, format.getSeparator(), uuiDsfromData, zipOutputStream, referenceValue)) ); @@ -129,9 +138,9 @@ public class DataCsvBuilder { } zipOutputStream.closeEntry(); } - Stream.of(application.getConfiguration().getAdditionalFiles()) + Stream.of(downloadDatasetQuery.application().getConfiguration().getAdditionalFiles()) .filter(a -> !a.isEmpty()) - .forEach(a -> repo.getRepository(application.getId()).additionalBinaryFile() + .forEach(a -> repo.getRepository(downloadDatasetQuery.application().getId()).additionalBinaryFile() .getAssociatedAdditionalFilesStream(uuiDsfromData.datasIds) .forEach(additionalFile -> { try { @@ -167,8 +176,8 @@ public class DataCsvBuilder { Map<String, Map<String, FieldType>> record = dataRow.getValues(); ImmutableList<String> rowAsRecord = columns.values().stream() .map(variableComponentSelect -> { - Map<String, FieldType> components = record.computeIfAbsent(variableComponentSelect.getVariable(), k -> Collections.emptyMap()); - FieldType value = components.getOrDefault(variableComponentSelect.getComponent(), StringType.getStringTypeFromStringValue("")); + Map<String, FieldType> components = record.computeIfAbsent(variableComponentSelect.variableComponentKey().variable(), k -> Collections.emptyMap()); + FieldType value = components.getOrDefault(variableComponentSelect.variableComponentKey().component(), StringType.getStringTypeFromStringValue("")); return value; }) .map(fieldType -> DateType.sorteableDateToFormattedDate(fieldType.toString())) @@ -186,7 +195,7 @@ public class DataCsvBuilder { ReferenceValue references) { try { final ReferenceImporterContext referenceImporterContext = uuiDsfromData - .getOrBuildReferenceImporterContext(zipOutputStream, separator, references, referenceService, application.getName()); + .getOrBuildReferenceImporterContext(zipOutputStream, separator, references, referenceService, downloadDatasetQuery.application().getName()); referenceImporterContext.writeReference(uuiDsfromData, references); if (counter.incrementAndGet() % 1000 == 0) { if (uuiDsfromData.getWriter() != null) { @@ -220,7 +229,7 @@ public class DataCsvBuilder { .flatMap(List::stream) .collect(Collectors.toSet()); }) - .forEach(referencedUUID -> repo.getRepository(application.getId()).referenceValue().getLinkedReferenceValues(referencedUUID) + .forEach(referencedUUID -> repo.getRepository(downloadDatasetQuery.application().getId()).referenceValue().getLinkedReferenceValues(referencedUUID) .stream() .forEach(refToAdd -> referencesValues.computeIfAbsent(refToAdd.getReferenceType(), k -> new LinkedList<>()).add(refToAdd)) ); @@ -228,7 +237,8 @@ public class DataCsvBuilder { } - private ImmutableMap<String, VariableComponentKey> getExportColumns(Configuration.FormatDescription format) { + private Map<String, VariableComponentKey> getExportColumns(Configuration.FormatDescription format, Set<VariableComponentKey> filters) { + final Set<VariableComponentKey> filtersToApply = Optional.ofNullable(filters).map(HashSet::new).orElseGet(HashSet::new); ImmutableMap<String, VariableComponentKey> valuesFromStaticColumns = format.getColumns().stream() .collect(ImmutableMap.toImmutableMap(Configuration.ColumnBindingDescription::getHeader, Configuration.ColumnBindingDescription::getBoundTo)); ImmutableMap<String, VariableComponentKey> valuesFromConstants = format.getConstants().stream() @@ -243,7 +253,11 @@ public class DataCsvBuilder { .putAll(valuesFromConstants) .putAll(valuesFromHeaderPatterns) .putAll(valuesFromRepeatedColumns) - .build(); + .build() + .entrySet() + .stream() + .filter(entry-> filtersToApply.contains(entry.getValue())) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } public DataCsvBuilder withZipOutputStream(ZipOutputStream zipOutputStream) { diff --git a/src/main/java/fr/inra/oresing/rest/OreSiHandler.java b/src/main/java/fr/inra/oresing/rest/OreSiHandler.java index 1d051d8dd24dc5b1dec7fa42ffbe895636080d4b..c7c0b4a8b89f24a28b583f7c0f2d4e39b08d10d7 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiHandler.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiHandler.java @@ -3,6 +3,7 @@ package fr.inra.oresing.rest; import fr.inra.oresing.OreSiAnonymousRequestClient; import fr.inra.oresing.OreSiRequestClient; import fr.inra.oresing.OreSiUserRequestClient; +import fr.inra.oresing.persistence.roles.OreSiRole; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -32,6 +33,20 @@ public class OreSiHandler implements HandlerInterceptor { return true; } + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + log.debug("afterCompletion for role {}", getRole()); + HandlerInterceptor.super.afterCompletion(request, response, handler, ex); + } + + private String getRole() { + return Optional.ofNullable(requestContext) + .map(OreSiApiRequestContext::getRequestClient) + .map(OreSiRequestClient::role) + .map(OreSiRole::getAsSqlRole) + .orElse("with no role"); + } + /** * Si un utilisateur est authentifié, on enregistre son rôle de le contexte */ @@ -63,7 +78,8 @@ public class OreSiHandler implements HandlerInterceptor { @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { - log.debug("postHandle for {}", requestContext); + log.debug("postHandle for role {}", getRole() + ); requestContext.reset(); } diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java index 88119724dfb8575f6101da3cbda997660af79095..cb8d83481e118d1fb53b91dd4e83582b1a3d0c25 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java @@ -6,6 +6,7 @@ import com.google.common.base.Strings; import com.google.common.collect.*; import fr.inra.oresing.checker.*; import fr.inra.oresing.checker.type.FieldType; +import fr.inra.oresing.checker.type.NullType; import fr.inra.oresing.checker.type.ReferenceType; import fr.inra.oresing.model.*; import fr.inra.oresing.model.additionalfiles.AdditionalFilesInfos; @@ -36,11 +37,17 @@ import fr.inra.oresing.rest.model.rightsrequest.RightsRequestInfos; import fr.inra.oresing.rest.model.synthesis.SynthesisResult; import fr.inra.oresing.rest.reactive.ReactiveProgression; import fr.inra.oresing.rest.reactive.ReactiveResult; -import fr.inra.oresing.rest.reactive.ReactiveTypeError; import fr.inra.oresing.rest.reactive.ReactiveTypeResult; import fr.inra.oresing.rest.rightsrequest.BadRightsRequestInfosQuery; import fr.inra.oresing.rest.rightsrequest.BadRightsRequestOrUUIDQuery; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -61,7 +68,6 @@ import java.io.IOException; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.time.Duration; import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -118,50 +124,23 @@ public class OreSiResources { } @GetMapping(value = "/applications", produces = MediaType.APPLICATION_NDJSON_VALUE) - public ResponseEntity<StreamingResponseBody> getApplications(HttpServletResponse response, @RequestParam(required = false, defaultValue = "") String[] filter) { + public Flux<ReactiveResult> getApplications(@RequestParam(required = false, defaultValue = "") String[] filter) { List<ApplicationInformation> filters = Arrays.stream(filter) .map(s -> ApplicationInformation.valueOf(s)) .collect(Collectors.toList()); - return buildFluxRequestJDJson(response, fluxSink -> { + return buildFluxRequestJDJson(fluxSink -> { ReactiveProgression.GetApplicationProgression progression = new ReactiveProgression.GetApplicationProgression(0L, fluxSink); service.getApplications(progression, filters); }); } - private ResponseEntity<StreamingResponseBody> buildFluxRequestJDJson(HttpServletResponse response, Consumer<FluxSink<ReactiveResult>> fluxSink) { - JsonRowMapper<ReactiveResult> mapper = new JsonRowMapper(); - mapper.disableInsensitiveProperties(); - StreamingResponseBody streamResponseBody = out -> { - Flux.create((Consumer<FluxSink<ReactiveResult>>) fluxSink1 -> { - try { - fluxSink.accept(fluxSink1); - } catch (Exception e) { - fluxSink1.next(new ReactiveTypeError(e)); - fluxSink1.complete(); - } - }) - .subscribe(reactiveResult -> { - try { - out.write(mapper.toJson(reactiveResult).getBytes(StandardCharsets.UTF_8)); - out.write("\n".getBytes(StandardCharsets.UTF_8)); - out.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - }; - response.setContentType(MediaType.APPLICATION_NDJSON_VALUE); - response.addHeader("Pragma", "no-cache"); - response.addHeader("Expires", "0"); - - return ResponseEntity.ok() - .contentType(MediaType.APPLICATION_NDJSON) - .body(streamResponseBody); + private Flux<ReactiveResult> buildFluxRequestJDJson(Consumer<FluxSink<ReactiveResult>> fluxSink) { + return Flux.create(fluxSink); } @PostMapping(value = "/validate-configuration", produces = MediaType.APPLICATION_NDJSON_VALUE) - public ResponseEntity<StreamingResponseBody> validateConfiguration(HttpServletResponse response, @RequestParam("file") MultipartFile file) throws IOException { - return buildFluxRequestJDJson(response, fluxSink -> { + public Flux<ReactiveResult> validateConfiguration(@RequestParam("file") MultipartFile file) throws IOException { + return buildFluxRequestJDJson(fluxSink -> { ReactiveProgression.CreateApplicationProgression progression = new ReactiveProgression.CreateApplicationProgression(0l, fluxSink); ConfigurationParsingResult validationResult = service.validateConfiguration(progression, file); fluxSink.next(new ReactiveTypeResult(validationResult)); @@ -170,14 +149,13 @@ public class OreSiResources { } @PostMapping(value = "/applications/{name}", produces = MediaType.APPLICATION_NDJSON_VALUE) - public ResponseEntity<StreamingResponseBody> createApplication(HttpServletResponse response, - @PathVariable("name") String name, - @RequestParam(name = "comment", defaultValue = "") String comment, - @RequestParam("file") MultipartFile file) throws IOException, BadApplicationConfigurationException { + public Flux<ReactiveResult> createApplication(@PathVariable("name") String name, + @RequestParam(name = "comment", defaultValue = "") String comment, + @RequestParam("file") MultipartFile file) throws IOException, BadApplicationConfigurationException { if (!RelationalService.IdentifierTest.identifierForApplicationName(name)) { throw new BadLabelNameException(BadLabelNameException.LabelType.APPLICATION, name); } - return buildFluxRequestJDJson(response, fluxSink -> { + return buildFluxRequestJDJson(fluxSink -> { ReactiveProgression.CreateApplicationProgression progression = new ReactiveProgression.CreateApplicationProgression(0L, fluxSink); service.createApplication(progression, name, file, comment); }); @@ -331,11 +309,11 @@ public class OreSiResources { } @PostMapping(value = "/applications/{nameOrId}/configuration", produces = MediaType.APPLICATION_NDJSON_VALUE) - public ResponseEntity<StreamingResponseBody> changeConfiguration(HttpServletResponse response, @PathVariable("nameOrId") String nameOrId, - @RequestParam("file") MultipartFile file, - @RequestParam(name = "comment", defaultValue = "") String comment) throws IOException, BadApplicationConfigurationException { + public Flux<ReactiveResult> changeConfiguration(@PathVariable("nameOrId") String nameOrId, + @RequestParam("file") MultipartFile file, + @RequestParam(name = "comment", defaultValue = "") String comment) throws IOException, BadApplicationConfigurationException { - return buildFluxRequestJDJson(response, fluxSink -> { + return buildFluxRequestJDJson(fluxSink -> { if (file.isEmpty()) { fluxSink.error(new IllegalArgumentException("EmptyFile")); } @@ -551,7 +529,11 @@ public class OreSiResources { try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) { service.getAdditionalFilesNamesZipStream(zipOutputStream, nameOrId, additionalFilesInfos); } catch (IOException ioe) { - log.error("Exception while reading and streaming data {} ", ioe); + switch (LocaleContextHolder.getLocale().getLanguage()) { + case "fr" -> log.error("Exception lors de la lecture et du streaming de données {} ", ioe); + case "en" -> log.error("Exception while reading and streaming data {} ", ioe); + case null, default -> log.error("Exception while reading and streaming data {} ", ioe); + } } }; response.setHeader("Content-Disposition", "attachment; filename=additionalFiles.zip"); @@ -595,19 +577,257 @@ public class OreSiResources { /** * export as JSON */ - @GetMapping(value = "/applications/{nameOrId}/data/{dataType}", produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(parameters = { + @Parameter( + name = "downloadDatasetQuery", + ref = "fr.inra.oresing.persistence.requestBuilder.datatype.DownloadDatasetQuery", + required = false, + explode = Explode.TRUE + + ) + }, + description = "Return an extraction of data of datatType 'dataType' of application 'nameOrId'" + ) + + @GetMapping(value = "/applications/{nameOrId}/data/{dataType}/json", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<GetDataResult> getAllDataJson( @PathVariable("nameOrId") String nameOrId, @PathVariable("dataType") String dataType, + @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = @Content( + examples = {@ExampleObject( + name = "general case", + summary = "You can provide an optional json with variableComponentSelects, variableComponentFilters, variableComponentOrderBy", + value = + """ + { + "variableComponentSelects": [...], + "variableComponentFilters": [...], + "variableComponentOrderBy": [...] + } + """)} + ), + ref = "fr.inra.oresing.persistence.requestBuilder.datatype.DownloadDatasetQuery", + description = "A parameter that can reduce, filter or order the result", + required = false + ) + @Parameter( + examples = { + @ExampleObject( + name = "general case", + ref = "fr.inra.oresing.model.data.DownloadDatasetQuery.class", + value = "General case. You can provide an optional json with variableComponentSelects, rowIds, authorizationDescriptions, variableComponentFilters, variableComponentOrderBy", + description = + """ + { + "variableComponentSelects": [...], + "variableComponentFilters": [...], + "variableComponentOrderBy": [...] + } + """), + @ExampleObject( + name = "reduce by select", + ref = "fr.inra.oresing.model.data.DownloadDatasetQuery.class", + value = "Select by. You can provide an optional json with variableComponentSelects", + description = + """ + { + "variableComponentSelects": [ + { + "variable": "espece", + "component": "value" + } + ] + } + """), + @ExampleObject( + name = "order by", + ref = "fr.inra.oresing.model.data.DownloadDatasetQuery.class", + value = "Order by. You can provide an optional json with variableComponentOrderBy", + description = + """ + { + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "espece", + "component": "value" + }, + "order": "ASC" + } + ] + } + """), + @ExampleObject( + name = "select by rowIds", + ref = "fr.inra.oresing.model.data.DownloadDatasetQuery.class", + value = "Find by RowIds. You can provide an optional json with rowIds", + description = + """ + { + "rowIds" : [ + "59ae5fec-8ed5-495b-b201-5475ea50906e" + ] + } + """), + @ExampleObject( + name = "select by authorization", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Find by authorizations. You can provide an optional json with authorization", + description = + """ + { + "authorizationDescriptions": [ + { + "timeScope": { + "from": "1984-01-01", + "to": "1984-01-03" + }, + "requiredAuthorizations": [ + { + "projet": "projet_manche", + "localization": "plateforme" + } + ] + } + ] + } + """), + @ExampleObject( + name = "select filter by filter (like '%filter%')", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by filter. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "site", + "component": "bassin" + }, + "filter": "nivelle" + }, + { + "variableComponentKey": { + "variable": "espece", + "component": "value" + }, + "filter": "tr" + } + ] + } + """), + @ExampleObject( + name = "select filter by filter with regexp (~ '^[ao]m+)", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by RegExp. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "espece", + "component": "value" + }, + "filter": "^[ao]m+", + "isRegExp": "true" + } + ] + } + """), + @ExampleObject( + name = "select filter by filter date (With declared pattern)", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by date. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "date", + "component": "time" + }, + "filter": "12:45:00" + } + ] + } + """), + @ExampleObject( + name = "select filter by filter date by interval (With declared pattern)", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by interval of date. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "type": "date", + "format": "dd/MM/yyyy", + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + } + ] + } + """), + @ExampleObject( + name = "select filter by filter numeric)", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by numeric. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "filter": 42 + } + ] + } + """), + @ExampleObject( + name = "select filter by filter numeric by interval)", + ref = "fr.inra.oresing.model.data.authorizationDescriptions.class", + value = "Select by interval of numeric. You can provide an optional json with variableComponentFilters", + description = + """ + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "intervalValues": { + "from": 25, + "to": 30 + } + } + ] + } + """) + }, + name = "downloadDatasetQuery", + required = false, + description = "An objetc for reduce, filter and order result" + ) @RequestParam(value = "downloadDatasetQuery", required = false) String params) { LinkedHashSet<String> orderedVariables = buildOrderedVariables(nameOrId, dataType); - DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params); - Locale locale = Optional.ofNullable(downloadDatasetQuery) - .map(DownloadDatasetQuery::getLocale) - .map(Locale::new) - .orElseGet(LocaleContextHolder::getLocale); - List<DataRow> list = service.findData(downloadDatasetQuery, nameOrId, dataType); + fr.inra.oresing.model.data.DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params, nameOrId, dataType); + List<DataRow> list = service.findData(downloadDatasetQuery); ImmutableSet<String> variables = list.stream() .limit(1) .map(DataRow::getValues) @@ -645,24 +865,28 @@ public class OreSiResources { list.stream() .limit(1) .forEach(dataRow -> { - Set<UUID> refIds = dataRow.getRefsLinkedTo().get(((VariableComponentKey) lineCheckerWarper.getTarget()).variable()).get(((VariableComponentKey) lineCheckerWarper.getTarget()).component()); - requiredreferencesValues.values().stream() - .filter(k -> refIds != null) - .map(l -> - l.stream() - .filter(referenceValue -> refIds.contains(referenceValue.getId())) - .findFirst()) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst() - .ifPresent(referenceValue -> { - referenceLineCheckers.put( - variableComponentKey, - new ReferenceLineCheckerDisplay( - (LineCheckerWarperResult) lineCheckerWarper, - referenceValue - )); - }); + Map<String, Set<UUID>> uuids = dataRow.getRefsLinkedTo().get(((VariableComponentKey) lineCheckerWarper.getTarget()).variable()); + if (uuids != null) { + Set<UUID> refIds = uuids + .get(((VariableComponentKey) lineCheckerWarper.getTarget()).component()); + requiredreferencesValues.values().stream() + .filter(k -> refIds != null) + .map(l -> + l.stream() + .filter(referenceValue -> refIds.contains(referenceValue.getId())) + .findFirst()) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst() + .ifPresent(referenceValue -> { + referenceLineCheckers.put( + variableComponentKey, + new ReferenceLineCheckerDisplay( + (LineCheckerWarperResult) lineCheckerWarper, + referenceValue + )); + }); + } }) ); } @@ -683,7 +907,10 @@ public class OreSiResources { String component = componentEntry.getKey(); rows .computeIfAbsent(variable, k -> new HashMap<>()) - .put(component, componentEntry.getValue().toJsonForFrontend()); + .put(component, Optional.ofNullable(componentEntry) + .map(Map.Entry::getValue) + .map(FieldType::toJsonForFrontend) + .orElse(NullType.INSTANCE)); } } return new DataRowResult(dataRow.getRowId(), rows, dataRow.getRefsLinkedTo(), dataRow.getTotalRows(), dataRow.getRowNumber()); @@ -709,9 +936,8 @@ public class OreSiResources { throw new DeleteOnrepositoryApplicationNotAllowedException(); } - DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params); - ; - List<UUID> deletedData = service.deleteData(downloadDatasetQuery, nameOrId, dataType); + fr.inra.oresing.model.data.DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params, nameOrId, dataType); + List<UUID> deletedData = service.deleteData(downloadDatasetQuery); return ResponseEntity.ok(deletedData.stream().map(UUID::toString).collect(Collectors.joining(","))); } @@ -749,14 +975,29 @@ public class OreSiResources { @PathVariable("nameOrId") String nameOrId, @PathVariable("dataType") String dataType, @RequestParam(value = "downloadDatasetQuery", required = false) String params) throws IOException { - DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params); - String locale = downloadDatasetQuery != null && downloadDatasetQuery.getLocale() != null ? downloadDatasetQuery.getLocale() : LocaleContextHolder.getLocale().getLanguage(); + fr.inra.oresing.model.data.DownloadDatasetQuery downloadDatasetQuery = deserialiseParamDownloadDatasetQuery(params, nameOrId, dataType); StreamingResponseBody streamResponseBody = out -> { try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) { - service.getDataZip(zipOutputStream, downloadDatasetQuery, nameOrId, dataType, locale); - + try { + service.getDataZip(zipOutputStream, downloadDatasetQuery); + } catch (Exception e) { + response.setStatus(400); + switch (LocaleContextHolder.getLocale().getLanguage()) { + case "fr" -> + response.setHeader("Erreur chargement", "Exception lors de la lecture et du streaming des données %1$s ".formatted(e.getMessage())); + case "en" -> + response.setHeader("Error loading", "Exception while reading and streaming data %1$s ".formatted(e.getMessage())); + case null, default -> + response.setHeader("Error loading", "Exception while reading and streaming data %1$s ".formatted(e.getMessage())); + } + response.flushBuffer(); + } } catch (IOException ioe) { - log.error("Exception while reading and streaming data {} ", ioe); + switch (LocaleContextHolder.getLocale().getLanguage()) { + case "fr" -> log.error("Exception lors de la lecture et du streaming de données {} ", ioe); + case "en" -> log.error("Exception while reading and streaming data {} ", ioe); + case null, default -> log.error("Exception while reading and streaming data {} ", ioe); + } } }; response.setContentType("application/zip"); @@ -770,9 +1011,17 @@ public class OreSiResources { .body(streamResponseBody); } - private DownloadDatasetQuery deserialiseParamDownloadDatasetQuery(String params) { + private fr.inra.oresing.model.data.DownloadDatasetQuery deserialiseParamDownloadDatasetQuery(String params, String applicationNameOrID, String dataType) { try { - return params != null ? new ObjectMapper().readValue(params, DownloadDatasetQuery.class) : null; + DownloadDatasetQuery downloadDatasetQuery = params != null ? new JsonRowMapper<DownloadDatasetQuery>().toObject(params, DownloadDatasetQuery.class) : new DownloadDatasetQuery(); + Application application = service.getApplication(applicationNameOrID); + downloadDatasetQuery.setApplication(application); + downloadDatasetQuery.setDataType(dataType); + Locale locale = Optional.ofNullable(downloadDatasetQuery) + .map(DownloadDatasetQuery::getLocale) + .map(Locale::new) + .orElseGet(LocaleContextHolder::getLocale); + return DownloadDatasetQuery.build(downloadDatasetQuery); } catch (IOException e) { throw new BadDownloadDatasetQuery(e.getMessage()); } diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index 478c0edaf62f62f607981e7eda6bd06e8812535f..a06b1526c61bb5086a221ea259810e98c44377a9 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -1,3 +1,4 @@ + package fr.inra.oresing.rest; import com.google.common.base.Charsets; @@ -18,6 +19,7 @@ import fr.inra.oresing.model.*; import fr.inra.oresing.model.additionalfiles.AdditionalBinaryFile; import fr.inra.oresing.model.additionalfiles.AdditionalFilesInfos; import fr.inra.oresing.model.chart.OreSiSynthesis; +import fr.inra.oresing.model.data.DownloadDatasetQueryNoFilter; import fr.inra.oresing.model.rightsrequest.RightsRequest; import fr.inra.oresing.persistence.*; import fr.inra.oresing.persistence.flyway.MigrateService; @@ -35,7 +37,7 @@ import fr.inra.oresing.rest.model.additionalfiles.CreateAdditionalFileRequest; import fr.inra.oresing.rest.model.application.ApplicationResult; import fr.inra.oresing.rest.model.application.ConfigurationParsingResult; import fr.inra.oresing.rest.model.authorization.*; -import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; +import fr.inra.oresing.model.data.DownloadDatasetQuery; import fr.inra.oresing.rest.model.rightsrequest.*; import fr.inra.oresing.rest.reactive.*; import fr.inra.oresing.rest.validationcheckresults.DateValidationCheckResult; @@ -61,7 +63,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.FileCopyUtils; import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartFile; -import reactor.core.publisher.FluxSink; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.annotation.Nullable; @@ -272,7 +274,7 @@ public class OreSiService { } } } - validateStoredData(new DownloadDatasetQuery(nameOrId, app, null, dataType, null, null, null, null, null, null)); + validateStoredData(new DownloadDatasetQueryNoFilter(app, dataType, new DownloadDatasetQuery.OutPut(Locale.FRANCE, 0L,null),null,null)); return application.getId(); } @@ -285,20 +287,21 @@ public class OreSiService { } private void validateStoredData(DownloadDatasetQuery downloadDatasetQuery) { - Application application = downloadDatasetQuery.getApplication(); - String dataType = downloadDatasetQuery.getDataType(); + Application application = downloadDatasetQuery.application(); + String dataType = downloadDatasetQuery.dataType(); ImmutableSet<LineCheckerWarper> lineCheckers = checkerFactory.getLineCheckers(application, dataType); Consumer<Datum> validateRow = line -> { lineCheckers.forEach(lineChecker -> { ValidationCheckResult validationCheckResult = lineChecker.check(line); - Preconditions.checkState(validationCheckResult.isSuccess(), "erreur de validation d'une donnée stockée " + validationCheckResult); + Preconditions.checkState(validationCheckResult.isSuccess(), + "erreur de validation d'une donnée stockée " + validationCheckResult); }); }; repo.getRepository(application).data() - .findAllByDataTypeStream(downloadDatasetQuery) + .findAllByDataTypeFlux(downloadDatasetQuery) .map(DataRow::getValues) .map(Datum::fromMapMapOfFieldType) - .forEach(validateRow); + .subscribe(validateRow); } private ReactiveProgression.ChangeOrCreateApplicationProgression changeApplicationConfiguration(ReactiveProgression.ChangeOrCreateApplicationProgression progression, @@ -488,7 +491,7 @@ public class OreSiService { dataStream .filter(Objects::nonNull) .peek(k -> { - if (lines.incrementAndGet() % 1000 == 0) { + if (lines.incrementAndGet() % 10000 == 0) { log.debug(String.format("%d %d", lines.get(), Duration.between(debut, Instant.now()).getSeconds())); } }) @@ -681,9 +684,9 @@ public class OreSiService { String component = variableComponentKey.component(); FieldType value = entry2.getValue(); FieldType copy = value.copy(); - if (copy.getValue() == null) { + /*if (copy.getValue() == null) { copy = StringType.getStringTypeFromStringValue(""); - } + }*/ toStore.computeIfAbsent(variable, k -> new LinkedHashMap<>()).put(component, copy); Optional.ofNullable(refsLinkedTo.get(variableComponentKey)) .ifPresent(uuids -> refsLinkedToToStore @@ -1085,14 +1088,10 @@ public class OreSiService { return computeDefaultValueFn; } - public void getDataZip(ZipOutputStream zipOutputStream, DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType, String locale) throws IOException { - final Application application = getApplication(nameOrId); - DownloadDatasetQuery downloadDatasetQueryCopy = DownloadDatasetQuery.buildDownloadDatasetQuery(downloadDatasetQuery, nameOrId, dataType, application); - Stream<DataRow> datas = findDataStream(downloadDatasetQueryCopy, nameOrId, dataType); + public void getDataZip(ZipOutputStream zipOutputStream, DownloadDatasetQuery downloadDatasetQuery) throws IOException { + Flux<DataRow> datas = findDataFlux(downloadDatasetQuery); DataCsvBuilder.getDataCsvBuilder() - .withApplication(application) - .withDatatype(dataType) - .withDownloadDatasetQuery(downloadDatasetQueryCopy) + .withDownloadDatasetQuery(downloadDatasetQuery) .withCheckerFactory(checkerFactory) .withReferenceService(referenceService) .withZipOutputStream(zipOutputStream) @@ -1131,17 +1130,14 @@ public class OreSiService { ); } - public List<DataRow> findData(DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType) { - return findDataStream(downloadDatasetQuery, nameOrId, dataType) - .collect(Collectors.toList()); + public List<DataRow> findData(fr.inra.oresing.model.data.DownloadDatasetQuery downloadDatasetQuery) { + return findDataFlux(downloadDatasetQuery).collectList().block(); } /* public void writeData(FluxSink<ReactiveResult> fluxSink, DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType) { - downloadDatasetQuery = DownloadDatasetQuery.buildDownloadDatasetQuery(downloadDatasetQuery, nameOrId, dataType, getApplication(nameOrId)); authenticationService.setRoleForClient(); - String applicationNameOrId = downloadDatasetQuery.getApplicationNameOrId(); - Application app = getApplication(applicationNameOrId); + Application app = downloadDatasetQuery.getApplication(); if (Optional.of(app.getConfiguration()) .map(Configuration::getDataTypes) .map(datatypes -> datatypes.get(dataType)) @@ -1168,29 +1164,24 @@ public class OreSiService { } */ - public Stream<DataRow> findDataStream(DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType) { - downloadDatasetQuery = DownloadDatasetQuery.buildDownloadDatasetQuery(downloadDatasetQuery, nameOrId, dataType, getApplication(nameOrId)); + public Flux<DataRow> findDataFlux(DownloadDatasetQuery downloadDatasetQuery) { authenticationService.setRoleForClient(); - String applicationNameOrId = downloadDatasetQuery.getApplicationNameOrId(); - Application app = getApplication(applicationNameOrId); + Application app = downloadDatasetQuery.application(); if (Optional.of(app.getConfiguration()) .map(Configuration::getDataTypes) - .map(datatypes -> datatypes.get(dataType)) + .map(datatypes -> datatypes.get(downloadDatasetQuery.dataType())) .map(Configuration.DataTypeDescription::getTags) .map(tags -> tags.stream().anyMatch(tag -> Configuration.HIDDEN_TAG.equals(tag))) .orElse(true)) { - return Stream.of(); + return Flux.empty(); } - Stream<DataRow> data = repo.getRepository(app).data().findAllByDataTypeStream(downloadDatasetQuery); - return data; + return repo.getRepository(app).data().findAllByDataTypeFlux(downloadDatasetQuery); } @Transactional(readOnly = false) - public List<UUID> deleteData(DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType) { - downloadDatasetQuery = DownloadDatasetQuery.buildDownloadDatasetQuery(downloadDatasetQuery, nameOrId, dataType, getApplication(nameOrId)); + public List<UUID> deleteData(DownloadDatasetQuery downloadDatasetQuery) { authenticationService.setRoleForClient(); - String applicationNameOrId = downloadDatasetQuery.getApplicationNameOrId(); - Application app = getApplication(applicationNameOrId); + Application app = downloadDatasetQuery.application(); List<UUID> data = repo.getRepository(app).data().deleteDataType(downloadDatasetQuery); return data; } @@ -1710,7 +1701,9 @@ public class OreSiService { public UniquenessKeys(Datum datum, List<VariableComponentKey> uniquenessDescription) { this.uniquenessDescription = uniquenessDescription; this.values = uniquenessDescription.stream() - .map(variableComponentKey -> Optional.ofNullable(datum).map(datum1 -> datum1.get(variableComponentKey)).orElse(StringType.getStringTypeFromStringValue(""))) + .map(variableComponentKey -> Optional.ofNullable(datum) + .map(datum1 -> datum1.get(variableComponentKey)) + .orElse(StringType.getStringTypeFromStringValue(""))) .map(FieldType::copy) .collect(Collectors.toList()); } @@ -1838,4 +1831,4 @@ public class OreSiService { return uniquenessKeyMap; } } -} \ No newline at end of file +} diff --git a/src/main/java/fr/inra/oresing/rest/ReferenceImporter.java b/src/main/java/fr/inra/oresing/rest/ReferenceImporter.java index d66d114c248dac32e75ca6295aa659357ab9a035..b84310d8ce8f63a27c6db65b0a1f9932ecaee6e5 100644 --- a/src/main/java/fr/inra/oresing/rest/ReferenceImporter.java +++ b/src/main/java/fr/inra/oresing/rest/ReferenceImporter.java @@ -93,8 +93,7 @@ abstract class ReferenceImporter { for (LineCheckerWarper lineChecker : referenceImporterContext.getLineCheckers()) { if (recursionStrategy instanceof WithRecursion) { - if (lineChecker.getUnderlyingType() instanceof ReferenceType) { - ReferenceType ReferenceType = (ReferenceType) lineChecker.getUnderlyingType(); + if (lineChecker.getUnderlyingType() instanceof ReferenceType ReferenceType) { Map<Ltree, UUID> map2 = ((WithRecursion) recursionStrategy).afterPreloadReferenceUuids; Map<Ltree, UUID> map1 = ReferenceType.getReferenceValues(); ImmutableMap.Builder<Ltree, UUID> builder = ImmutableMap.builder(); diff --git a/src/main/java/fr/inra/oresing/rest/RelationalService.java b/src/main/java/fr/inra/oresing/rest/RelationalService.java index 89a4abaa08a1d2936fd041c16c607084da0cfda6..b832fefd29c680dd24c9370af7e71f52efd493c8 100644 --- a/src/main/java/fr/inra/oresing/rest/RelationalService.java +++ b/src/main/java/fr/inra/oresing/rest/RelationalService.java @@ -5,12 +5,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import fr.inra.oresing.checker.CheckerFactory; -import fr.inra.oresing.checker.LineCheckerWarper; -import fr.inra.oresing.checker.Multiplicity; -import fr.inra.oresing.checker.type.FieldType; -import fr.inra.oresing.checker.type.ListType; -import fr.inra.oresing.checker.type.ReferenceType; +import fr.inra.oresing.checker.*; +import fr.inra.oresing.checker.type.*; import fr.inra.oresing.model.Application; import fr.inra.oresing.model.Configuration; import fr.inra.oresing.model.ReferenceColumn; @@ -18,9 +14,12 @@ import fr.inra.oresing.model.VariableComponentKey; import fr.inra.oresing.persistence.*; import fr.inra.oresing.persistence.roles.OreSiRightOnApplicationRole; import fr.inra.oresing.rest.exceptions.views.FieldNameTooLongForSqlFieldException; -import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.util.Strings; +import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; @@ -179,7 +178,7 @@ public class RelationalService implements InitializingBean, DisposableBean { return new SchemaCreationCommand(app, sqlSchema, views, viewStrategy); } - private List<ViewCreationCommand> getViewsForDataTypes(SqlSchemaForRelationalViewsForApplication sqlSchema, Application application) { + List<ViewCreationCommand> getViewsForDataTypes(SqlSchemaForRelationalViewsForApplication sqlSchema, Application application) { List<ViewCreationCommand> views = new LinkedList<>(); @@ -191,88 +190,72 @@ public class RelationalService implements InitializingBean, DisposableBean { Configuration.DataTypeDescription dataTypeDescription = entry.getValue(); ImmutableMap<VariableComponentKey, LineCheckerWarper> checkerPerVariableComponentKeys = checkerFactory.getLineCheckers(application, dataType).stream() .collect(ImmutableMap.toImmutableMap(rlc -> (VariableComponentKey) rlc.getTarget(), Function.identity())); - Map<VariableComponentKey, LineCheckerWarper> referenceCheckers = - Maps.transformValues( - Maps.filterValues(checkerPerVariableComponentKeys, checker -> checker.getUnderlyingType() instanceof ReferenceType), - checker -> checker - ); - Map<VariableComponentKey, SqlPrimitiveType> sqlTypesPerVariableComponentKey = - Maps.transformValues(checkerPerVariableComponentKeys, LineCheckerWarper::getSqlType); - - Set<String> referenceColumnIds = new LinkedHashSet<>(); - Set<String> selectClauseElements = new LinkedHashSet<>(); - Set<String> lateralClauseJoinElements = new LinkedHashSet<>(); - - String dataTableName = "my_data"; - String dataAfterDataGroupsMergingQuery = repository.getRepository(application).data().getSqlToMergeData(DownloadDatasetQuery.buildDownloadDatasetQuery(null, appId.toString(), dataType, application)); - String withClause = "WITH " + dataTableName + " AS (" + dataAfterDataGroupsMergingQuery + ")"; - - for (VariableComponentKey variableComponentKey : dataTypeDescription.doGetAllVariableComponents()) { - String variable = variableComponentKey.variable(); - String component = variableComponentKey.component(); - String escapedVariableName = StringUtils.replace(variable, "'", "''"); - String escapedComponentName = StringUtils.replace(component, "'", "''"); - SqlPrimitiveType sqlType = sqlTypesPerVariableComponentKey.getOrDefault(variableComponentKey, SqlPrimitiveType.TEXT); - String sqlTypeForVariableComponent = sqlType.getSql(); - if (checkerPerVariableComponentKeys.containsKey(variableComponentKey)) { - LineCheckerWarper checker = checkerPerVariableComponentKeys.get(variableComponentKey); - FieldType underlyingType = checker.getUnderlyingType(); - sqlTypeForVariableComponent = switch (underlyingType.getClass().getSimpleName()) { - case "DateType" -> "composite_date::timestamp"; - default -> sqlType.getSql(); - }; - sqlTypeForVariableComponent = String.format("%s%s", sqlTypeForVariableComponent, (checker.getFieldType() instanceof ListType) ? "[]" : ""); - - } - String selectClausePattern; - if (sqlType.isEmptyStringValidValue()) { - selectClausePattern = "jsonb_extract_path_text(%s.dataValues, '%s', '%s')::%s %s"; - } else { - selectClausePattern = "nullif(jsonb_extract_path_text(%s.dataValues, '%s', '%s'), '')::%s %s"; - } - String selectClauseElement = String.format( - selectClausePattern, - dataTableName, - escapedVariableName, - escapedComponentName, - sqlTypeForVariableComponent, - new IdentifierTestForvariableComponent(variableComponentKey).toColumnName().testAndQuote() - ); - selectClauseElements.add(selectClauseElement); - - if (referenceCheckers.containsKey(variableComponentKey)) { - LineCheckerWarper lineCheckerWarper = referenceCheckers.get(variableComponentKey); - FieldType underlyingType = lineCheckerWarper.getUnderlyingType(); - String checkerSqlType = switch (underlyingType.getClass().getSimpleName()) { - case "DateType" -> "composite_date::timestamp"; - default -> sqlType.getSql(); - }; - String columnName = new IdentifierTestForvariableComponent(variableComponentKey).toColumnName().testAndQuote(); - String columnNameForVariableComponentWithReferenceChecker = new IdentifierTestForvariableComponent(variableComponentKey).toColumnNameForVariableComponentWithReferenceChecker().testAndQuote(); - String selectFromLateralClause = String.format("%s.%s::uuid %s", - columnName, - columnName, - columnNameForVariableComponentWithReferenceChecker - ); - String lateralClause = String.format("lateral (SELECT jsonb_array_elements_text(coalesce(%s.refsLinkedTo #> '{%s,%s}', '[null]'::jsonb)) %s) %s", - dataTableName, - escapedVariableName, - escapedComponentName, - columnName, - columnName + Map<VariableComponentKey, SQLComponent.SqlViewPrimitiveType> sqlTypesPerVariableComponentKey = + Maps.transformValues(checkerPerVariableComponentKeys, SQLComponent.SqlViewPrimitiveType::getSqlType); + List<SQLVariableForData> sqlVariablesForData = new LinkedList<>(); + List<SQLVariableForRefsLinkedTo> sqlVariablesForRefsLinkedTos = new LinkedList<>(); + Map<String, List<SQLComponent>> sqlComponentMapForData = new HashMap<>(); + Map<String, List<SQLComponent>> sqlComponentMapForRefsLinkedTo = new HashMap<>(); + + dataTypeDescription.doGetAllVisibleVariableComponents().stream() + .forEach(variableComponentKey -> { + SQLComponent sqlComponent = new SQLComponent( + variableComponentKey.component(), + sqlTypesPerVariableComponentKey.getOrDefault(variableComponentKey, SQLComponent.SqlViewPrimitiveType.DEFAULT) + ); + sqlComponentMapForData.computeIfAbsent(variableComponentKey.variable(), k -> new LinkedList<SQLComponent>()) + .add(sqlComponent); + if (sqlComponent.sqlType().cast().contains("LTREE")) { + sqlComponentMapForRefsLinkedTo + .computeIfAbsent(variableComponentKey.variable(), k -> new LinkedList<SQLComponent>()) + .add(sqlComponent); + } + }); + sqlComponentMapForData.entrySet().stream() + .forEach(e -> sqlVariablesForData.add(new SQLVariableForData(e.getKey(), e.getValue()))); + sqlComponentMapForRefsLinkedTo.entrySet().stream() + .forEach(e -> sqlVariablesForRefsLinkedTos.add(new SQLVariableForRefsLinkedTo(e.getKey(), e.getValue()))); + String viewSql = """ + + \sWITH my_data AS ( + SELECT data.rowid, + jsonb_object_agg(data.datavalues) AS datavalues, + jsonb_object_agg(data.refslinkedto) AS refslinkedto + FROM %1$s.data + GROUP BY data.rowid + ), + variables as ( + SELECT rowid %2$s + FROM\s + my_data, + %3$s + ) + select rowid technicalId, + %4$s + from + variables%5$s + """ + .formatted( + application.getName(), + ListUtils.union(sqlVariablesForData, sqlVariablesForRefsLinkedTos).stream() + .map(SQLVariable::getSqlSelectForWithVariable) + .distinct() + .collect(Collectors.joining(", ", ", ", "")), + ListUtils.union(sqlVariablesForData, sqlVariablesForRefsLinkedTos).stream() + .map(SQLVariable::buildLateralForWithVariable) + .distinct() + .collect(Collectors.joining(", ")), + sqlVariablesForData.stream() + .map(sqlVariableForData -> sqlVariableForData.getSqlSelectForWithVariableJoiningRefsLinkedTo(sqlVariablesForRefsLinkedTos)) + .flatMap(List::stream) + .collect(Collectors.joining(",\n " + ) + ), + ListUtils.union(sqlVariablesForData, sqlVariablesForRefsLinkedTos).stream() + .map(SQLVariable::buildLateralForSelect) + .distinct() + .collect(Collectors.joining(",\n ", ",\n ", "")) ); - selectClauseElements.add(selectFromLateralClause); - lateralClauseJoinElements.add(lateralClause); - } - } - - String selectClause = "SELECT " + String.join(", ", selectClauseElements); - - String fromClause = "FROM " + dataTableName - + (lateralClauseJoinElements.isEmpty() ? " " : ",\n") - + Joiner.on(",\n").join(lateralClauseJoinElements); - - String viewSql = String.join("\n", withClause, selectClause, fromClause); SqlTable view = sqlSchema.forDataType(dataType); views.add(new ViewCreationCommand(view, viewSql)); @@ -280,7 +263,8 @@ public class RelationalService implements InitializingBean, DisposableBean { return views; } - private List<ViewCreationCommand> getDenormalizedViewsForDataTypes(SqlSchemaForRelationalViewsForApplication sqlSchema, Application application) { + private List<ViewCreationCommand> getDenormalizedViewsForDataTypes(SqlSchemaForRelationalViewsForApplication + sqlSchema, Application application) { List<ViewCreationCommand> views = new LinkedList<>(); for (Map.Entry<String, Configuration.DataTypeDescription> entry : application.getConfiguration().getDataTypes().entrySet()) { String dataType = entry.getKey(); @@ -292,13 +276,17 @@ public class RelationalService implements InitializingBean, DisposableBean { Set<String> fromClauseJoinElements = new LinkedHashSet<>(); String dataTableName = sqlSchema.forDataType(dataType).getSqlIdentifier(); - - for (LineCheckerWarper referenceChecker : referenceCheckers.values()) { + Set<VariableComponentKey> visibles = dataTypeDescription.doGetAllVisibleVariableComponents(); + List<LineCheckerWarper> lineCheckerWarpers = referenceCheckers.entrySet().stream() + .filter(e -> visibles.contains(e.getKey())) + .map(Map.Entry::getValue) + .toList(); + for (LineCheckerWarper referenceChecker : lineCheckerWarpers) { String refType = ((ReferenceType) referenceChecker.getUnderlyingType()).getRefType(); // especes VariableComponentKey variableComponentKey = (VariableComponentKey) referenceChecker.getTarget(); String quotedViewName = sqlSchema.forReferenceType(refType).getSqlIdentifier(); - String foreignKeyColumnName = new IdentifierTestForvariableComponent(variableComponentKey).toTechnicalIdColumnName().testAndQuote(); + String foreignKeyColumnName = new IdentifierTestForvariableComponent(variableComponentKey).toTechnicalIdColumnName().testAndQuote().toLowerCase(); String alias = new IdentifierTestForvariableComponent(variableComponentKey).toAliasForColumnName().testAndQuote(); application.getConfiguration().getReferences().get(refType).doGetStaticColumns().stream() .map(referenceColumn -> alias + "." @@ -306,20 +294,26 @@ public class RelationalService implements InitializingBean, DisposableBean { + " as " + new IdentifierTestForvariableComponent(variableComponentKey).toColumnNameForVariableComponentInDenormalized(referenceColumn).testAndQuote()) .forEach(selectClauseReferenceElements::add); - fromClauseJoinElements.add("left outer join " + quotedViewName + " " - + alias + " on " + dataTableName + "." - + foreignKeyColumnName + "::uuid = " - + alias + "." + IdentifierTest.forStringIdentifier(refType).forId().testAndQuote() + "::uuid"); + fromClauseJoinElements.add(""" + left outer join %1$s %2$s + on %2$s.%3$s::uuid = any(%4$s.%5$s)""" + .formatted( + quotedViewName, + alias.toLowerCase(), + IdentifierTest.forStringIdentifier(refType).forId().testAndQuote(), + dataTableName, + foreignKeyColumnName + ) + ); } - Set<String> selectClauseElements = new LinkedHashSet<>(selectClauseReferenceElements); - - dataTypeDescription.doGetAllVariableComponents().stream() + Set<String> selectClauseElements = dataTypeDescription.doGetAllVisibleVariableComponents().stream() .map(variableComponentKey -> new IdentifierTestForvariableComponent(variableComponentKey) .toColumnName() - .testAndQuote()) + .testAndQuote() + .toLowerCase()) .map(columnName -> dataTableName + "." + columnName) - .forEach(selectClauseElements::add); + .collect(Collectors.toSet()); String selectClause = "select " + String.join(", ", selectClauseElements); @@ -334,7 +328,8 @@ public class RelationalService implements InitializingBean, DisposableBean { return views; } - private List<ViewCreationCommand> getViewsForReferences(SqlSchemaForRelationalViewsForApplication sqlSchema, Application app) { + private List<ViewCreationCommand> getViewsForReferences(SqlSchemaForRelationalViewsForApplication + sqlSchema, Application app) { UUID appId = app.getId(); List<ViewCreationCommand> views = new LinkedList<>(); for (Map.Entry<String, Configuration.ReferenceDescription> entry : app.getConfiguration().getReferences().entrySet()) { @@ -517,13 +512,102 @@ public class RelationalService implements InitializingBean, DisposableBean { } + sealed interface SQLVariable permits SQLVariableForData, SQLVariableForRefsLinkedTo { + String name(); + + List<SQLComponent> sqlComponents(); + + default String getType() { + return switch (this) { + case SQLVariableForData sqlVariableForData -> "datavalues"; + case SQLVariableForRefsLinkedTo sqlVariableForRefsLinkedTo -> "refslinkedto"; + }; + } + + default String getSelect(SQLComponent component) { + + return switch (this) { + case SQLVariableForData sqlVariableForData -> """ + "%1$s_datavalues_row"."%2$s"::%3$s "%4$s_%5$s" """.formatted( + name().replace("'", "''"), + component.name(), + component.sqlType().cast(), + name().toLowerCase().replaceAll("[' ]", "_"), + component.name().toLowerCase().replaceAll("[' ]", "_") + ); + case SQLVariableForRefsLinkedTo sqlVariableForRefsLinkedTo -> "refslinkedto"; + }; + } + + default String getSelectForRef(SQLComponent component) { + return switch (this) { + case SQLVariableForData sqlVariableForData -> """ + "%1$s_refslinkedto_row"."%2$s"::%3$s "%4$s_%5$s_technicalid" """.formatted( + name().replace("'", "''"), + component.name(), + "UUID[]", + name().toLowerCase().replaceAll("[' ]", "_"), + component.name().toLowerCase().replaceAll("[' ]", "_") + ); + case SQLVariableForRefsLinkedTo sqlVariableForRefsLinkedTo -> "refslinkedto"; + }; + } + + default String buildLateralForWithVariable() { + return """ + LATERAL ( + SELECT "%1$s" as "%1$s_%2$s" + FROM jsonb_to_record(%2$s) AS "%1$s"("%1$s" jsonb) + ) "%1$s_%2$s_row" """ + .formatted( + name().replace("'", "''"), + getType() + ); + } + + default String getSqlSelectForWithVariable() { + return """ + "%1$s_%2$s_row".*""".formatted(name().replace("'", "''"), getType()); + } + + default String buildLateralForSelect() { + return """ + LATERAL ( + SELECT * + FROM jsonb_to_record("%1$s_%2$s") + AS "%1$s" %3$s + ) "%1$s_%2$s_row" """ + .formatted( + name().replace("'", "''"), + getType(), + sqlComponents() + .stream() + .map(sqlComponent -> switch (this) { + case SQLVariableForData sqlVariableForData -> sqlComponent.toRecordDefinition(); + case SQLVariableForRefsLinkedTo sqlVariableForRefsLinkedTo -> + sqlComponent.toRecordDefinitionForRef(); + }) + .collect(Collectors.joining( + """ + ,\s + \s""", + """ + ( + \s""", + """ + )""" + ) + ) + ); + } + } + private record SchemaCreationCommand(Application application, SqlSchemaForRelationalViewsForApplication schema, List<ViewCreationCommand> views, ViewStrategy viewStrategy) { } - - private record ViewCreationCommand(SqlTable view, String sql) { + record ViewCreationCommand(SqlTable view, String sql) { } @@ -581,6 +665,10 @@ public class RelationalService implements InitializingBean, DisposableBean { String.format("\"%s\"", quotedIdentifier); } + public String testAndQuoteForRefsLinkedTo() { + return testAndQuote().replaceAll("^\"", "\"refs_linked_to_"); + } + public String testAndReturnIdentifier() { return identifier; } @@ -616,7 +704,6 @@ public class RelationalService implements InitializingBean, DisposableBean { } } - public static class IdentifierTestForvariableComponent { private VariableComponentKey variableComponentKey; @@ -656,4 +743,121 @@ public class RelationalService implements InitializingBean, DisposableBean { return toTechnicalIdColumnName(); } } + + record MultiplicitySelect(String select, String lateral) { + public static final String SIMPLE_SELECT_PATTERN = """ + (%1$s.dataValues #>> '{%2$s,%3$s}')::%4$s as %5$s"""; + public static final String SIMPLE_SELECT_PATTERN_WITH_NULL = """ + (NULLIF(%1$s.dataValues #>> '{%2$s,%3$s}', ''))::%4$s as %5$s"""; + static String SELECT_PATTERN_MANY = """ + array_agg(%1$s.v) over(partition by my_data.rowid) %1$s"""; + static String SELECT_PATTERN_MANY_WITH_NULL = """ + array_agg(NULLIF(%1$s.v, '')) over(partition by my_data.rowid) %1$s"""; + static String SELECT_LATERAL_MANY = """ + LEFT JOIN LATERAL(select ((jsonb_array_elements(%1$s.dataValues #> '{%2$s, %3$s}')) #>> '{}')::%4$s as v, rowid ) as %5$s using(rowid)"""; + static String SELECT_PATTERN_ID = """ + %1$s.v::uuid %2$s"""; + static String SELECT_LATERAL_ID = """ + LEFT JOIN LATERAL (SELECT jsonb_array_elements_text(coalesce(%1$s.refsLinkedTo #> '{%2$s,%3$s}', '[null]'::jsonb)) as v, rowid) %4$s using(rowid)"""; + } + + record SQLComponent(String name, SqlViewPrimitiveType sqlType) { + SQLComponent { + if (!Strings.isNotEmpty(name)) { + throw new IllegalArgumentException("name can't be null"); + } + Objects.requireNonNull(sqlType, "sqlType can't be empty"); + } + + String toRecordDefinition() { + String cast = sqlType().cast().contains("DATE")?"TEXT":sqlType().cast(); + return """ + "%1$s" %2$s%3$s """ + .formatted( + name().replace("[, ]", "''"), + cast, + sqlType().multiplicity() + ); + } + + String toRecordDefinitionForRef() { + return """ + "%1$s" UUID[] """ + .formatted( + name().replace("[, ]", "''"), + sqlType().multiplicity() + ); + } + + record SqlViewPrimitiveType(String cast, String multiplicity) { + + static SqlViewPrimitiveType DEFAULT = new SqlViewPrimitiveType("TEXT", ""); + + public static SqlViewPrimitiveType getSqlType(LineCheckerWarper checkerWarper) { + String multiplicity = switch (checkerWarper) { + case ManyCheckerWarper manyCheckerWarper -> "[]"; + case OneCheckerWarper oneCheckerWarper -> ""; + case null, default -> ""; + }; + return switch (checkerWarper.getUnderlyingType()) { + case ReferenceType referenceType -> + new SqlViewPrimitiveType("LTREE%1$s".formatted(multiplicity), multiplicity); + case BooleanType booleanType -> + new SqlViewPrimitiveType("BOOLEAN%1$s".formatted(multiplicity), multiplicity); + case DateType dateType -> + new SqlViewPrimitiveType("COMPOSITE_DATE%s::TIMESTAMP%1$s".formatted(multiplicity), multiplicity); + case FloatType floatType -> + new SqlViewPrimitiveType("NUMERIC%1$s".formatted(multiplicity), multiplicity); + case IntegerType integerType -> + new SqlViewPrimitiveType("INTEGER%1$s".formatted(multiplicity), multiplicity); + case null, default -> new SqlViewPrimitiveType("TEXT%1$s".formatted(multiplicity), multiplicity); + }; + } + } + } + + record SQLVariableForData(String name, List<SQLComponent> sqlComponents) implements SQLVariable { + SQLVariableForData { + if (Strings.isEmpty(name)) { + throw new IllegalArgumentException("name can't be null"); + } + if (CollectionUtils.isEmpty(sqlComponents)) { + throw new IllegalArgumentException("sqlComponents can't be empty"); + } + } + + public List<String> getSqlSelectForWithVariableJoiningRefsLinkedTo(List<SQLVariableForRefsLinkedTo> sqlVariablesForRefsLinkedTos) { + return sqlComponents().stream() + .map(component -> { + LinkedList<String> sqlVariables = new LinkedList<>(); + sqlVariables.add(this.getSelect(component)); + sqlVariables.addAll(sqlVariablesForRefsLinkedTos.stream() + .filter(sqlVariableForRefsLinkedTo -> sqlVariableForRefsLinkedTo.name().equals(name())) + .map(SQLVariableForRefsLinkedTo::sqlComponents) + .map(sqlComponents -> sqlComponents.stream() + .filter(componentRefs -> componentRefs.name().equals(component.name())) + .map(this::getSelectForRef) + .toList() + ) + .flatMap(List::stream) + .collect(Collectors.toList()) + ); + return sqlVariables; + } + ) + .flatMap(List::stream) + .toList(); + } + } + + record SQLVariableForRefsLinkedTo(String name, List<SQLComponent> sqlComponents) implements SQLVariable { + SQLVariableForRefsLinkedTo { + if (Strings.isEmpty(name)) { + throw new IllegalArgumentException("name can't be null"); + } + if (CollectionUtils.isEmpty(sqlComponents)) { + throw new IllegalArgumentException("sqlComponents can't be empty"); + } + } + } } diff --git a/src/main/java/fr/inra/oresing/rest/exceptions/SiOreIllegalArgumentException.java b/src/main/java/fr/inra/oresing/rest/exceptions/SiOreIllegalArgumentException.java index 1978b04982a524a6179f1b5e8e1f0aaca3284ac4..57dfe8dcd5fe7dccd215bd65d01c78c26d827eaf 100644 --- a/src/main/java/fr/inra/oresing/rest/exceptions/SiOreIllegalArgumentException.java +++ b/src/main/java/fr/inra/oresing/rest/exceptions/SiOreIllegalArgumentException.java @@ -10,7 +10,8 @@ import java.util.Map; @EqualsAndHashCode(callSuper = true) @Value -@JsonIgnoreProperties(value = { "stackTrace", "detailMassage", "cause", "depth", "suppressedExeceptions" })public class SiOreIllegalArgumentException extends IllegalArgumentException{ +@JsonIgnoreProperties(value = { "stackTrace", "detailMassage", "cause", "depth", "suppressedExeceptions" })public class +SiOreIllegalArgumentException extends IllegalArgumentException{ String message; Map<String, Object> params; } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/exceptions/data/BadDownloadDatasetQuery.java b/src/main/java/fr/inra/oresing/rest/exceptions/data/BadDownloadDatasetQuery.java index 5e1d5afc5365bd69bc077bd493f5956454e7752d..6560054007c279510f5a9135a4ce2492c398a128 100644 --- a/src/main/java/fr/inra/oresing/rest/exceptions/data/BadDownloadDatasetQuery.java +++ b/src/main/java/fr/inra/oresing/rest/exceptions/data/BadDownloadDatasetQuery.java @@ -2,8 +2,50 @@ package fr.inra.oresing.rest.exceptions.data; import fr.inra.oresing.OreSiTechnicalException; +import java.io.Serializable; +import java.util.Map; + public class BadDownloadDatasetQuery extends OreSiTechnicalException { + public static final String MISSING_INTERVAL_VALUE = "variableComponentFilter.missingIntervalValue"; + public static final String MISSING_TYPE_FOR_INTERVAL_VALUE = "variableComponentFilter.missingTypeForIntervalValue"; + public static final String MISSING_FORMAT_FOR_INTERVAL_VALUE = "variableComponentFilter.missingFormatForIntervalValue"; + public static final String MISSING_FORMAT_FOR_FILTER = "variableComponentFilter.missingFormatForFilter"; + public static final String MISSING_FILTER = "variableComponentFilter.missingFilter"; + public static final String MISSING_ID_FOR_UUID = "variableComponentFilter.missingIdForUUID"; + public static final String MISSING_COMPONENT_KEY_FOR_SEARCH = "variableComponentFilter.missingCoponentKeyForSearch"; + public static final String MISSING_COMPONENT_KEY_VARIABLE = "variableComponentFilter.missingCoponentKeyVariable"; + public static final String MISSING_COMPONENT_KEY_COMPONENT = "variableComponentFilter.missingCoponentKeyComponent"; + public static final String FILTER_BAD_FORMAT_FOR_START_DATE = "variableComponentFilter.badFormatForStartDate"; + public static final String FILTER_BAD_FORMAT_FOR_END_DATE = "variableComponentFilter.badFormatForEndDate"; + public static final String FILTER_BAD_FORMAT_BAD_RANGE_FOR_DATES = "variableComponentFilter.badrangeForDates"; + public static final String FILTER_BAD_FORMAT_FOR_START_TIME = "variableComponentFilter.badFormatForStartTime"; + public static final String FILTER_BAD_FORMAT_FOR_END_TIME = "variableComponentFilter.badFormatForEndTime"; + public static final String FILTER_BAD_FORMAT_BAD_RANGE_FOR_TIMES = "variableComponentFilter.badrangeForTimes"; + public static final String FILTER_BAD_FORMAT_FOR_START_DATE_TIME = "variableComponentFilter.badFormatForStartDateTime"; + public static final String FILTER_BAD_FORMAT_FOR_END_DATE_TIME = "variableComponentFilter.badFormatForEndDateTime"; + public static final String FILTER_BAD_FORMAT_BAD_RANGE_FOR_DATE_TIMES = "variableComponentFilter.badrangeForDateTimes"; + public static final String FILTER_BAD_FORMAT_FOR_START_NUMERIC = "variableComponentFilter.badFormatForStartNumeric"; + public static final String FILTER_BAD_FORMAT_FOR_END_NUMERIC = "variableComponentFilter.badFormatForEndNumeric"; + public static final String FILTER_BAD_FORMAT_BAD_RANGE_FOR_NUMERICS = "variableComponentFilter.badrangeForNumerics"; + public static final String FILTER_MISSING_FILTER_OR_INTERVAL = "variableComponentFilter.missingFilterOrInterval"; + public static final String FILTER_MISSING_VARIABLE_COMPONENT_FOR_DATATYPE = "variableComponentFilter.missingVariableComponentForDatatype"; + public static final String MISSING_FORMAT_FOR_FILTER_DATE = "variableComponentFilter.missingFormatForFilterDate"; + public static final String BAD_FORMAT_FOR_FILTER_DATE = "variableComponentFilter.badFormatForFilterDate"; + public static final String NOT_INTERVAL_VALUE_TYPE_FOR_VARIABLE_COMPONENT = "variableComponentFilter.notIntervalValueTypeForVariableComponent"; + public static final String BAD_PATTERN_FOR_DATE = "variableComponentFilter.badPatternForDate"; + + + public Map<String, Serializable> getParams() { + return params; + } + + Map<String, Serializable> params; public BadDownloadDatasetQuery(String message) { super(message); } + + public BadDownloadDatasetQuery(String message, Map<String, Serializable> params) { + super(message); + this.params = params; + } } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/model/data/DownloadDatasetQuery.java b/src/main/java/fr/inra/oresing/rest/model/data/DownloadDatasetQuery.java index 9807bcd4c84eb231ebe420046badb97cf39381c9..c0eda11f8ee706d009971dee4b4172954b423834 100644 --- a/src/main/java/fr/inra/oresing/rest/model/data/DownloadDatasetQuery.java +++ b/src/main/java/fr/inra/oresing/rest/model/data/DownloadDatasetQuery.java @@ -1,11 +1,20 @@ package fr.inra.oresing.rest.model.data; +import fr.inra.oresing.checker.CheckerType; +import fr.inra.oresing.checker.Multiplicity; import fr.inra.oresing.model.*; +import fr.inra.oresing.model.data.DownloadDatasetQueryAdvancedSearch; +import fr.inra.oresing.model.data.DownloadDatasetQueryByRowId; +import fr.inra.oresing.model.data.DownloadDatasetQueryNoFilter; +import fr.inra.oresing.model.data.DownloadDatasetQuerySimpleSearch; import fr.inra.oresing.persistence.DataRepository; import fr.inra.oresing.persistence.Ltree; import fr.inra.oresing.persistence.SqlSchemaForApplication; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; import lombok.Getter; import lombok.Setter; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.util.Strings; import javax.annotation.Nullable; import java.time.LocalDate; @@ -13,14 +22,14 @@ import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; +import static fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery.*; + @Getter @Setter public class DownloadDatasetQuery { Application application; - String applicationNameOrId; String dataType; - String reference; String locale; Long offset; Long limit; @@ -44,12 +53,10 @@ public class DownloadDatasetQuery { this.variableComponentFilters = variableComponentFilters; this.variableComponentOrderBy = variableComponentOrderBy; application = null; - applicationNameOrId = null; dataType = null; } - public DownloadDatasetQuery(String applicationNameOrId, Application application, Long offset, String dataType, @Nullable Set<VariableComponentKey> variableComponentSelects, @Nullable Set<VariableComponentFilters> variableComponentFilters, @Nullable Set<VariableComponentOrderBy> variableComponentOrderBy, Set<AuthorizationDescription> authorizationDescriptions, Long limit, Set<String> rowIds) { - this.applicationNameOrId = applicationNameOrId; + public DownloadDatasetQuery(Application application, Long offset, String dataType, @Nullable Set<VariableComponentKey> variableComponentSelects, @Nullable Set<VariableComponentFilters> variableComponentFilters, @Nullable Set<VariableComponentOrderBy> variableComponentOrderBy, Set<AuthorizationDescription> authorizationDescriptions, Long limit, Set<String> rowIds) { this.dataType = dataType; this.offset = offset; this.limit = limit; @@ -62,50 +69,131 @@ public class DownloadDatasetQuery { } - - public List<String> getHiddenVariables() { - return application.getConfiguration().getDataTypes().get(dataType).getData().entrySet().stream() - .filter(entry -> entry.getValue().isHidden()) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); + public DownloadDatasetQuery(Application application, String dataType) { + this.application = application; + this.dataType = dataType; } - public List<VariableComponentKey> getHiddenComponents() { - return application.getConfiguration().getDataTypes().get(dataType).getData().entrySet().stream() - .map(entryVariable -> { - String variableName = entryVariable.getKey(); - LinkedHashMap<String, Configuration.VariableComponentDescription> components = new LinkedHashMap<>(); - components.putAll(entryVariable.getValue().getComponents()); - components.putAll(entryVariable.getValue().getComputedComponents()); - return components.entrySet().stream() - .filter(entry -> entry.getValue() != null && entry.getValue().isHidden()) - .map(Map.Entry::getKey) - .map(componentName -> new VariableComponentKey(variableName, componentName)) - .collect(Collectors.toList()); - }) - .flatMap(List::stream) - .collect(Collectors.toList()); + public static fr.inra.oresing.model.data.DownloadDatasetQuery build(DownloadDatasetQuery downloadDatasetQuery) { + if (CollectionUtils.isNotEmpty(downloadDatasetQuery.rowIds)) { + return new DownloadDatasetQueryByRowId( + downloadDatasetQuery.getApplication(), + downloadDatasetQuery.dataType, + new DownloadDatasetQueryAdvancedSearch.OutPut( + Optional.ofNullable(downloadDatasetQuery.getLocale()) + .map(Locale::of) + .orElse(Locale.FRENCH), + downloadDatasetQuery.getOffset(), + downloadDatasetQuery.getLimit() + ), + downloadDatasetQuery.variableComponentSelects, + + Optional.ofNullable(downloadDatasetQuery.variableComponentOrderBy) + .map(variableComponentOrderBy -> variableComponentOrderBy.stream() + .map(variableComponentOrderBy1 -> VariableComponentOrderBy.build( + variableComponentOrderBy1, + downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()) + )) + .collect(Collectors.toSet()) + ).orElse(null), + downloadDatasetQuery.rowIds.stream() + .map(UUID::fromString) + .map(DownloadDatasetQueryByRowId.DataRowIds::new) + .collect(Collectors.toSet()) + ); + } else if (CollectionUtils.isNotEmpty(downloadDatasetQuery.authorizationDescriptions)) { + return new DownloadDatasetQuerySimpleSearch( + downloadDatasetQuery.getApplication(), + downloadDatasetQuery.dataType, + new DownloadDatasetQueryAdvancedSearch.OutPut( + Optional.ofNullable(downloadDatasetQuery.getLocale()) + .map(Locale::of) + .orElse(Locale.FRENCH), + downloadDatasetQuery.getOffset(), + downloadDatasetQuery.getLimit() + ), + downloadDatasetQuery.variableComponentSelects, + + Optional.ofNullable(downloadDatasetQuery.variableComponentOrderBy) + .map(variableComponentOrderBy -> variableComponentOrderBy.stream() + .map(variableComponentOrderBy1 -> VariableComponentOrderBy.build( + variableComponentOrderBy1, + downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()) + )) + .collect(Collectors.toSet()) + ).orElse(null), + downloadDatasetQuery.authorizationDescriptions.stream() + .map(AuthorizationDescription::build) + .collect(Collectors.toSet()) + ); + } else if (CollectionUtils.isNotEmpty(downloadDatasetQuery.variableComponentFilters)) { + return new DownloadDatasetQueryAdvancedSearch( + downloadDatasetQuery.getApplication(), + downloadDatasetQuery.dataType, + new DownloadDatasetQueryAdvancedSearch.OutPut( + Optional.ofNullable(downloadDatasetQuery.getLocale()) + .map(Locale::of) + .orElse(Locale.FRENCH), + downloadDatasetQuery.getOffset(), + downloadDatasetQuery.getLimit() + ), + downloadDatasetQuery.variableComponentSelects, + VariableComponentFilters.build( + downloadDatasetQuery.variableComponentFilters, + downloadDatasetQuery.authorizationDescriptions, + downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType())), + + Optional.ofNullable(downloadDatasetQuery.variableComponentOrderBy) + .map(variableComponentOrderBy -> variableComponentOrderBy.stream() + .map(variableComponentOrderBy1 -> VariableComponentOrderBy.build( + variableComponentOrderBy1, + downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()) + )) + .collect(Collectors.toSet()) + ).orElse(null) + ); + } else { + return new DownloadDatasetQueryNoFilter( + downloadDatasetQuery.getApplication(), + downloadDatasetQuery.dataType, + new DownloadDatasetQueryAdvancedSearch.OutPut( + Optional.ofNullable(downloadDatasetQuery.getLocale()) + .map(Locale::of) + .orElse(Locale.FRENCH), + downloadDatasetQuery.getOffset(), + downloadDatasetQuery.getLimit() + ), + downloadDatasetQuery.variableComponentSelects, + Optional.ofNullable(downloadDatasetQuery.variableComponentOrderBy) + .map(variableComponentOrderBy -> variableComponentOrderBy.stream() + .map(variableComponentOrderBy1 -> VariableComponentOrderBy.build( + variableComponentOrderBy1, + downloadDatasetQuery.getApplication().getConfiguration().getDataTypes().get(downloadDatasetQuery.getDataType()) + )) + .collect(Collectors.toSet()) + ).orElse(null) + ); + } } - public static DownloadDatasetQuery buildDownloadDatasetQuery(DownloadDatasetQuery downloadDatasetQuery, String nameOrId, String dataType, Application application) { - return downloadDatasetQuery == null ? - new DownloadDatasetQuery( - nameOrId, - application, null, dataType, - null, null, null, null, null,null) : - new DownloadDatasetQuery( - nameOrId == null ? downloadDatasetQuery.applicationNameOrId : nameOrId, - application, downloadDatasetQuery.offset, dataType == null ? downloadDatasetQuery.dataType : dataType, - downloadDatasetQuery.variableComponentSelects, downloadDatasetQuery.variableComponentFilters, downloadDatasetQuery.variableComponentOrderBy, downloadDatasetQuery.authorizationDescriptions, downloadDatasetQuery.limit, downloadDatasetQuery.rowIds - ); + // TODO- rest ou persistance enum ?!! (=^_^=) + public enum FieldType { + date, time, datetime, numeric, bool, reference; + + boolean isNumeric() { + return this == numeric; + } + + boolean isDate() { + return Set.of(date, time, datetime).contains(this); + } } + public enum FieldTypeForInterval {date, time, datetime, numeric} + public static class VariableComponentOrderBy { public VariableComponentKey variableComponentKey; public DataRepository.Order order; - public String type; - public String format; - public String key; public VariableComponentOrderBy() { } @@ -115,6 +203,18 @@ public class DownloadDatasetQuery { this.order = order; } + public static DownloadDatasetQueryAdvancedSearch.VariableComponentOrderBy build( + VariableComponentOrderBy variableComponentOrderBy, + Configuration.DataTypeDescription dataTypeDescription + ) { + return new DownloadDatasetQueryAdvancedSearch.VariableComponentOrderBy( + variableComponentOrderBy.variableComponentKey, + variableComponentOrderBy.order, + dataTypeDescription.getTypeForVariableComponentKey(variableComponentOrderBy.variableComponentKey) + + ); + } + public String getId() { return variableComponentKey == null ? null : variableComponentKey.getId(); } @@ -132,29 +232,164 @@ public class DownloadDatasetQuery { } } - @Getter @Setter public static class VariableComponentFilters { public VariableComponentKey variableComponentKey; public String filter; - public String type; - public String format; public IntervalValues intervalValues; public Boolean isRegExp = false; public VariableComponentFilters() { } - public VariableComponentFilters(VariableComponentKey variableComponentKey, String filter, String type, String format, IntervalValues intervalValues, Boolean isRegExp) { + public VariableComponentFilters(VariableComponentKey variableComponentKey, String filter, FieldType type, String format, IntervalValues intervalValues, Boolean isRegExp) { this.variableComponentKey = variableComponentKey; this.filter = filter; - this.type = type; - this.format = format; this.intervalValues = intervalValues; this.isRegExp = isRegExp; } + public static DownloadDatasetQueryAdvancedSearch.VariableComponentFilters build( + VariableComponentFilters variableComponentFilter, + Configuration.DataTypeDescription dataTypeDescription) throws BadDownloadDatasetQuery { + if (variableComponentFilter == null) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_FOR_SEARCH); + } else if (Strings.isEmpty(variableComponentFilter.variableComponentKey.variable())) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_VARIABLE); + } else if (Strings.isEmpty(variableComponentFilter.variableComponentKey.component())) { + throw new BadDownloadDatasetQuery(MISSING_COMPONENT_KEY_COMPONENT); + } + CheckerType.VariableComponentType formatForFieldTypeDate = Optional.ofNullable(dataTypeDescription) + .map(dd->dd.getTypeForVariableComponentKey(variableComponentFilter.variableComponentKey)) + .orElseGet(CheckerType.VariableComponentType.VariableComponentTextType::new); + Multiplicity multiplicity = variableComponentFilter.variableComponentKey.test(dataTypeDescription); + if (variableComponentFilter.intervalValues != null) { + return switch (formatForFieldTypeDate) { + case CheckerType.VariableComponentType.VariableComponentDateType( + String format, + DownloadDatasetQueryAdvancedSearch.FieldType fieldType + ) -> switch (fieldType) { + case date -> + new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByDate( + variableComponentFilter.variableComponentKey, + new DownloadDatasetQueryAdvancedSearch.IntervalValuesDate( + variableComponentFilter.getIntervalValues().getFrom(), + variableComponentFilter.getIntervalValues().getTo(), + format), + multiplicity + ); + case time -> + new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByTime( + variableComponentFilter.variableComponentKey, + new DownloadDatasetQueryAdvancedSearch.IntervalValuesTime(variableComponentFilter.intervalValues.from, + variableComponentFilter.getIntervalValues().to, + format), + multiplicity + ); + case datetime -> + new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByDateTime( + variableComponentFilter.variableComponentKey, + new DownloadDatasetQueryAdvancedSearch.IntervalValuesDateTime(variableComponentFilter.intervalValues.from, + variableComponentFilter.getIntervalValues().to, + format), + multiplicity + ); + default -> throw new IllegalStateException("Unexpected value: " + fieldType); + }; + case CheckerType.VariableComponentType.VariableComponentNumericType variableComponentNumericType -> new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByNumeric( + variableComponentFilter.variableComponentKey, + new DownloadDatasetQueryAdvancedSearch.IntervalValuesNumeric(variableComponentFilter.intervalValues.from, + variableComponentFilter.getIntervalValues().to), + multiplicity + ); + case null, default -> throw new BadDownloadDatasetQuery( + BadDownloadDatasetQuery.NOT_INTERVAL_VALUE_TYPE_FOR_VARIABLE_COMPONENT, + Map.of( + "variable", variableComponentFilter.variableComponentKey.variable(), + "component", variableComponentFilter.variableComponentKey.component() + ) + ); + }; + } else if (Strings.isNotEmpty(variableComponentFilter.getFilter())) { + return switch (formatForFieldTypeDate) { + case CheckerType.VariableComponentType.VariableComponentBooleanType variableComponentBooleanType -> { + yield null;// TODO -- + } + case CheckerType.VariableComponentType.VariableComponentDateType( + String format, + DownloadDatasetQueryAdvancedSearch.FieldType fieldType + ) -> switch (fieldType) { + case date -> new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByDate( + variableComponentFilter.variableComponentKey, + format, + variableComponentFilter.getFilter(), + multiplicity + ); + case time -> new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByTime( + variableComponentFilter.variableComponentKey, + format, + variableComponentFilter.getFilter(), + multiplicity + ); + case datetime -> new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByDateTime( + variableComponentFilter.variableComponentKey, + format, + variableComponentFilter.getFilter(), + multiplicity + ); + default -> throw new IllegalStateException("Unexpected value: " + fieldType); + }; + case CheckerType.VariableComponentType.VariableComponentNumericType variableComponentNumericType -> + new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByNumeric( + variableComponentFilter.variableComponentKey, + variableComponentFilter.getFilter(), + multiplicity + ); + case CheckerType.VariableComponentType.VariableComponentReferenceType variableComponentReferenceType -> + new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByReference( + variableComponentFilter.variableComponentKey, + variableComponentFilter.getFilter(), + multiplicity + ); + case null, default -> { + if (Optional.ofNullable(variableComponentFilter.isRegExp).orElse(false)) { + yield new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByRegexp( + variableComponentFilter.variableComponentKey, + variableComponentFilter.getFilter(), + multiplicity + ); + } else { + yield new DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByPlainText( + variableComponentFilter.variableComponentKey, + variableComponentFilter.getFilter(), + multiplicity + ); + } + } + }; + } + throw new BadDownloadDatasetQuery(FILTER_MISSING_FILTER_OR_INTERVAL, Map.of( + "variable", variableComponentFilter.getVariable(), + "component", variableComponentFilter.getComponent() + )); + } + + public static Set<DownloadDatasetQueryAdvancedSearch.VariableComponentFilters> build( + Set<VariableComponentFilters> variableComponentFilters, + Set<AuthorizationDescription> authorizationDescriptions, + Configuration.DataTypeDescription dataTypeDescription) { + if (CollectionUtils.isNotEmpty(variableComponentFilters)) { + return variableComponentFilters.stream() + .map(variableComponentFilter -> build( + variableComponentFilter, + dataTypeDescription)) + .collect(Collectors.toSet()); + } else { + return Set.of(new DownloadDatasetQueryAdvancedSearch.NoVariableComponentFilters()); + } + } + public String getId() { return variableComponentKey == null ? null : variableComponentKey.getId(); } @@ -170,14 +405,6 @@ public class DownloadDatasetQuery { public String getFilter() { return filter != null ? filter : null; } - - public Boolean isNumeric() { - return "numeric".equals(type); - } - - public Boolean isDate() { - return "date".equals(type); - } } @@ -187,11 +414,6 @@ public class DownloadDatasetQuery { public String from; public String to; - public IntervalValues(String from, String to) { - this.from = from; - this.to = to; - } - public IntervalValues() { } } @@ -202,9 +424,26 @@ public class DownloadDatasetQuery { * */ public static class AuthorizationDescription { + private IntervalValues timeScope; private List<Map<String, Ltree>> requiredAuthorizations = new LinkedList<>(); + public static DownloadDatasetQuerySimpleSearch.AuthorizationDescription build( + AuthorizationDescription authorizationDescription + ) { + List<List<DownloadDatasetQuerySimpleSearch.RequiredAuthorization>> requiredAuthorizations = + authorizationDescription.getRequiredAuthorizations().stream() + .map(map -> map.entrySet().stream() + .map(entry -> new DownloadDatasetQuerySimpleSearch.RequiredAuthorization(entry.getKey(), entry.getValue())) + .toList()) + .toList(); + + return new DownloadDatasetQuerySimpleSearch.AuthorizationDescription( + new DownloadDatasetQuerySimpleSearch.IntervalValues(authorizationDescription.getTimeScope().getFrom(), authorizationDescription.getTimeScope().getTo()), + requiredAuthorizations + ); + } + public List<Authorization> toAuthorization(Application application, SqlSchemaForApplication schema) { DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); String sqlStart = schema.getSqlIdentifier() + ".isauthorized(data.authorization, array["; @@ -212,7 +451,7 @@ public class DownloadDatasetQuery { LocalDateTimeRange localDateTimeRange = timeScope == null ? LocalDateTimeRange.always() : LocalDateTimeRange.getTimeScope( (timeScope.from == null ? null : LocalDate.parse(timeScope.from, dateFormatter)), (timeScope.to == null ? null : LocalDate.parse(timeScope.to, dateFormatter))); - if(requiredAuthorizations.isEmpty()){ + if (requiredAuthorizations.isEmpty()) { requiredAuthorizations.add(new HashMap<>()); } return requiredAuthorizations.stream() diff --git a/src/main/resources/migration/main/V1__init_schema.sql b/src/main/resources/migration/main/V1__init_schema.sql index 2986496cc31333d4a2dd1cec1902614c2fc4417d..f594ba04ba43107de3d8b1a1175d1c7fb9f62943 100644 --- a/src/main/resources/migration/main/V1__init_schema.sql +++ b/src/main/resources/migration/main/V1__init_schema.sql @@ -131,7 +131,7 @@ CREATE POLICY "superadmin_Application_insert" CREATE AGGREGATE jsonb_object_agg(jsonb) (SFUNC = 'jsonb_concat', STYPE = jsonb, INITCOND = '{}'); -CREATE AGGREGATE aggregate_by_array_concatenation(anyarray) (SFUNC = 'array_cat', STYPE = anyarray, INITCOND = '{}'); +CREATE AGGREGATE aggregate_by_array_concatenation(anycompatiblearray) (SFUNC = 'array_cat', STYPE = anycompatiblearray, INITCOND = '{}'); create type COMPOSITE_DATE as ( diff --git a/src/test/java/fr/inra/oresing/checker/CheckerTypeTest.java b/src/test/java/fr/inra/oresing/checker/CheckerTypeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..83c3699a5f375c27d3048bd7c321547239ab0007 --- /dev/null +++ b/src/test/java/fr/inra/oresing/checker/CheckerTypeTest.java @@ -0,0 +1,46 @@ +package fr.inra.oresing.checker; + +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +class CheckerTypeTest { + @Test + public void TestFormatTime(){ + String pattern = "HH:mm:ss"; + CheckerType.TypeOfDate expected = CheckerType.TypeOfDate.TIME; + Assert.assertEquals(expected,CheckerType.getFieldType(pattern)); + + } + + @Test + public void TestFormatDateFR(){ + String pattern = "dd/MM/yyyy"; + CheckerType.TypeOfDate expected = CheckerType.TypeOfDate.DATE; + Assert.assertEquals(expected,CheckerType.getFieldType(pattern)); + + } + + @Test + public void TestFormatDateEN(){ + String pattern = "yyyy-MM-dd"; + CheckerType.TypeOfDate expected = CheckerType.TypeOfDate.DATE; + Assert.assertEquals(expected,CheckerType.getFieldType(pattern)); + + } + + + @Test + public void TestFormatDateTime(){ + String pattern = "dd/MM/yyyy HH:mm:ss"; + CheckerType.TypeOfDate expected = CheckerType.TypeOfDate.DATETIME; + Assert.assertEquals(expected,CheckerType.getFieldType(pattern)); + + } + + @Test + public void TestBadPattern(){ + String pattern = "yyyy ss"; + Assert.assertNull(CheckerType.getFieldType(pattern)); + } + +} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/ACBB_TEST.java b/src/test/java/fr/inra/oresing/rest/ACBB_TEST.java deleted file mode 100644 index 36f4af0103df02bb17335f1165ee3b068b1fdbd7..0000000000000000000000000000000000000000 --- a/src/test/java/fr/inra/oresing/rest/ACBB_TEST.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.inra.oresing.rest; - -class ACBB_TEST { -} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java index d0b88da6078c143308f020d0b43f128d65f77be4..a0135f341dd2fad0a5c10b2aeed0a4e08f656c9f 100644 --- a/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java +++ b/src/test/java/fr/inra/oresing/rest/AuthorizationResourcesTest.java @@ -10,6 +10,7 @@ import fr.inra.oresing.rest.exceptions.authentication.NotApplicationCreatorRight import fr.inra.oresing.rest.reactive.ReactiveTypeError; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import com.google.common.base.Strings; @@ -18,7 +19,6 @@ import org.hamcrest.core.IsEqual; import static org.junit.jupiter.api.Assertions.*; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -146,7 +146,7 @@ public class AuthorizationResourcesTest { } { - mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur") + mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur/json") .cookie(authReaderCookie) .accept(MediaType.TEXT_PLAIN)) .andExpect(status().is4xxClientError()); @@ -347,7 +347,7 @@ public class AuthorizationResourcesTest { } { - String json = mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur") + String json = mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur/json") .cookie(authReaderCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -434,7 +434,7 @@ public class AuthorizationResourcesTest { } { - String json = mockMvc.perform(get("/api/v1/applications/hautefrequence/data/hautefrequence") + String json = mockMvc.perform(get("/api/v1/applications/hautefrequence/data/hautefrequence/json") .cookie(authReaderCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -462,7 +462,7 @@ public class AuthorizationResourcesTest { } { - String json = Objects.requireNonNull(mockMvc.perform(get("/api/v1/applications/hautefrequence/data/hautefrequence") + String json = Objects.requireNonNull(mockMvc.perform(get("/api/v1/applications/hautefrequence/data/hautefrequence/json") .cookie(authReaderCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is4xxClientError()) @@ -530,9 +530,19 @@ public class AuthorizationResourcesTest { try (InputStream configurationFile = getClass().getResourceAsStream(fixtures.getMonsoreApplicationConfigurationResourceName());) { MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", configurationFile); List<ReactiveTypeError> errors = fixtures.getErrors(fixtures.loadApplication(configuration, applicationCreatorCookies, "monsore", "")); - final Map validationCheckResult = ( ((LinkedHashMap) errors.get(0).result())); - assertEquals("NO_RIGHT_FOR_APPLICATION_CREATION", validationCheckResult.get("message" )); - assertEquals("monsore", validationCheckResult.get("applicationName" )); + final Map validationCheckResult = (((LinkedHashMap) errors.get(0).result())); + fail(); + } catch (Throwable e) { + switch (e) { + case NotApplicationCreatorRightsException notApplicationCreatorRightsException -> { + + assertEquals("NO_RIGHT_FOR_APPLICATION_CREATION", notApplicationCreatorRightsException.getMessage()); + assertEquals("monsore", notApplicationCreatorRightsException.applicationName); + + } + case null, default -> throw new RuntimeException(e); + } + } } @@ -569,8 +579,17 @@ public class AuthorizationResourcesTest { try (InputStream configurationFile = getClass().getResourceAsStream(fixtures.getMonsoreApplicationConfigurationResourceName());) { MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", configurationFile); List<ReactiveTypeError> errors = fixtures.getErrors(fixtures.loadApplication(configuration, applicationCreatorCookies, "monsore", "")); - assertEquals("NO_RIGHT_FOR_APPLICATION_CREATION", ((Map) errors.get(0).result()).get("message")); - assertEquals("monsore", ((Map) errors.get(0).result()).get("applicationName")); + fail(); + } catch (Throwable e) { + switch (e) { + case NotApplicationCreatorRightsException notApplicationCreatorRightsException -> { + + assertEquals("NO_RIGHT_FOR_APPLICATION_CREATION", notApplicationCreatorRightsException.getMessage()); + assertEquals("monsore", notApplicationCreatorRightsException.applicationName); + + } + case null, default -> throw new RuntimeException(e); + } } } } @@ -582,7 +601,6 @@ public class AuthorizationResourcesTest { namedParameterJdbcTemplate.update("grant \"superadmin\" to \"" + dbUserResult.getUserId().toString() + "\"", Map.of()); } - @NotNull private String[] getApplicationsFlux(Cookie cookie, String... filter) throws Exception { return mockMvc.perform(asyncDispatch(mockMvc.perform(get("/api/v1/applications") .accept(MediaType.APPLICATION_NDJSON_VALUE) @@ -594,4 +612,4 @@ public class AuthorizationResourcesTest { .andReturn().getResponse().getContentAsString() .split("\n"); } -} \ No newline at end of file +} diff --git a/src/test/java/fr/inra/oresing/rest/Fixtures.java b/src/test/java/fr/inra/oresing/rest/Fixtures.java index a95cb2d2f7c555ccdce49bcc8c7457e041dd0621..76c96465ebca4783a293a452ccc9387c3c477935 100644 --- a/src/test/java/fr/inra/oresing/rest/Fixtures.java +++ b/src/test/java/fr/inra/oresing/rest/Fixtures.java @@ -6,6 +6,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; +import com.jayway.jsonpath.JsonPath; import fr.inra.oresing.OreSiTechnicalException; import fr.inra.oresing.model.OreSiUser; import fr.inra.oresing.persistence.AuthenticationService; @@ -67,7 +68,7 @@ public class Fixtures { List<ReactiveTypeInfo> getInfos(MvcResult result) throws UnsupportedEncodingException { return Optional.ofNullable(getReactiveResultFromResult(result) - .get(ReactiveType.REACTIVE_INFO)) + .get(ReactiveType.REACTIVE_INFO)) .orElseGet(List::of) .stream() .map(ReactiveTypeInfo.class::cast) @@ -76,7 +77,7 @@ public class Fixtures { List<ReactiveTypeError> getErrors(MvcResult result) throws UnsupportedEncodingException { return Optional.ofNullable(getReactiveResultFromResult(result) - .get(ReactiveType.REACTIVE_ERROR)) + .get(ReactiveType.REACTIVE_ERROR)) .orElseGet(List::of) .stream() .map(ReactiveTypeError.class::cast) @@ -89,7 +90,7 @@ public class Fixtures { List<ReactiveTypeResult> getResults(MvcResult result) throws UnsupportedEncodingException { return Optional.ofNullable(getReactiveResultFromResult(result) - .get(ReactiveType.REACTIVE_RESULT)) + .get(ReactiveType.REACTIVE_RESULT)) .orElseGet(List::of) .stream() .map(ReactiveTypeResult.class::cast) @@ -98,7 +99,7 @@ public class Fixtures { List<ReactiveTypeProgress> getProgress(MvcResult result) throws UnsupportedEncodingException { return Optional.ofNullable(getReactiveResultFromResult(result) - .get(ReactiveType.REACTIVE_PROGRESS)) + .get(ReactiveType.REACTIVE_PROGRESS)) .orElseGet(List::of) .stream() .map(ReactiveTypeProgress.class::cast) @@ -130,6 +131,24 @@ public class Fixtures { ); } + Exception loadApplicationWithError(MockMultipartFile file, + Cookie cookie, + String applicationName) throws Exception { + MvcResult result = mockMvc + .perform(multipart("/api/v1/applications/{applicationName}", applicationName) + .file(file) + .cookie(cookie) + ) + .andExpect(request().asyncStarted()) + .andReturn(); + if (result.getAsyncResult() instanceof Exception) { + return (Exception) result.getAsyncResult(); + } + return mockMvc.perform(asyncDispatch(result)) + .andReturn() + .getResolvedException(); + } + MvcResult validateApplication(MockMultipartFile file, Cookie cookie) throws Exception { ResultActions result = mockMvc.perform( @@ -149,20 +168,24 @@ public class Fixtures { MvcResult loadApplication(MockMultipartFile file, Cookie cookie, String applicationName, - String comment) throws Exception { - ResultActions result = mockMvc.perform( - multipart("/api/v1/applications/{applicationName}", applicationName) - .file(file) - .param("comment", comment != null ? comment : "") - .accept(MediaType.APPLICATION_NDJSON) - .cookie(cookie) - ); - return mockMvc.perform(asyncDispatch( - result - .andExpect(request().asyncStarted()) - .andReturn()) - ) - .andReturn(); + String comment) throws Throwable { + try { + ResultActions result = mockMvc.perform( + multipart("/api/v1/applications/{applicationName}", applicationName) + .file(file) + .param("comment", comment != null ? comment : "") + .accept(MediaType.APPLICATION_NDJSON) + .cookie(cookie) + ); + return mockMvc.perform(asyncDispatch( + result + .andExpect(request().asyncStarted()) + .andReturn()) + ) + .andReturn(); + } catch (Exception e) { + throw e.getCause(); + } } @@ -674,6 +697,16 @@ public class Fixtures { result = loadApplication(configuration, authCookie, (applicationName == null ? "monsore" : applicationName), (applicationName == null ? "monsore" : applicationName)); return getIdFromApplicationResult(result); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public Exception createApplicationMonSoreWithError(Cookie authCookie, String applicationName) throws Exception { + try (InputStream configurationFile = getClass().getResourceAsStream(getMonsoreApplicationConfigurationResourceName())) { + MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", configurationFile); + return loadApplicationWithError(configuration, authCookie, (applicationName == null ? "monsore" : applicationName)); + } } @@ -709,6 +742,8 @@ public class Fixtures { MockMultipartFile configuration = new MockMultipartFile("file", "fake-app.yaml", "text/plain", configurationFile); getIdFromApplicationResult(loadApplication(configuration, authCookie, "fakeapp", "fakeapp")); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel @@ -739,6 +774,8 @@ public class Fixtures { try (InputStream configurationFile = getClass().getResourceAsStream(getAcbbApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "acbb.yaml", "text/plain", configurationFile); getIdFromApplicationResult(loadApplication(configuration, authCookie, "acbb", "acbb")); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel @@ -820,6 +857,8 @@ public class Fixtures { try (InputStream configurationFile = getClass().getResourceAsStream(getHauteFrequenceApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "hautefrequence.yaml", "text/plain", configurationFile); loadApplication(configuration, authCookie, "hautefrequence", "hautefrequence"); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel @@ -887,6 +926,8 @@ public class Fixtures { MockMultipartFile configuration = new MockMultipartFile("file", "olac.yaml", "text/plain", configurationFile); loadApplication(configuration, authCookie, "olac", "olac"); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel @@ -1045,6 +1086,8 @@ public class Fixtures { try (InputStream configurationFile = getClass().getResourceAsStream(getForetApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "foret.yaml", "text/plain", configurationFile); loadApplication(configuration, authCookie, "foret", "foret"); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel @@ -1113,6 +1156,8 @@ public class Fixtures { try (InputStream in = getClass().getResourceAsStream(getRecursivityApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "recursivity.yaml", "text/plain", in); loadApplication(configuration, authCookie, "recursivite", "recursivite"); + } catch (Throwable e) { + throw new RuntimeException(e); } String response; diff --git a/src/test/java/fr/inra/oresing/rest/HAUTE_FREQUENCE_TEST.java b/src/test/java/fr/inra/oresing/rest/HAUTE_FREQUENCE_TEST.java deleted file mode 100644 index 3db7519c242a9eb67e8fbfee10425b9df6afe85b..0000000000000000000000000000000000000000 --- a/src/test/java/fr/inra/oresing/rest/HAUTE_FREQUENCE_TEST.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.inra.oresing.rest; - -final class HAUTE_FREQUENCE_TEST { -} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/ORE_TEST.java b/src/test/java/fr/inra/oresing/rest/ORE_TEST.java deleted file mode 100644 index f6c44de60deebdd0f23810c7963be5c98e64d658..0000000000000000000000000000000000000000 --- a/src/test/java/fr/inra/oresing/rest/ORE_TEST.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.inra.oresing.rest; - -public class ORE_TEST { -} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/OTHERS_TEST.java b/src/test/java/fr/inra/oresing/rest/OTHERS_TEST.java deleted file mode 100644 index 2e2e5e87e46bf86bb00188a138717d51c580d4d3..0000000000000000000000000000000000000000 --- a/src/test/java/fr/inra/oresing/rest/OTHERS_TEST.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.inra.oresing.rest; - -public class OTHERS_TEST { -} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java index aaa2246d0cc28938a9e5e5ff6ea9eac4eabc13c3..5e3fdf64a9d7a86b5a601accbfd9eaf3216c504f 100644 --- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java +++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java @@ -2,6 +2,7 @@ package fr.inra.oresing.rest; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Charsets; +import com.google.common.base.Strings; import com.google.common.io.Resources; import com.jayway.jsonpath.JsonPath; import fr.inra.oresing.OreSiNg; @@ -19,13 +20,14 @@ import fr.inra.oresing.rest.exceptions.authentication.NotApplicationCreatorRight import fr.inra.oresing.rest.exceptions.configuration.BadApplicationConfigurationException; import fr.inra.oresing.rest.exceptions.data.DeleteOnrepositoryApplicationNotAllowedException; import fr.inra.oresing.rest.model.application.ApplicationResult; -import fr.inra.oresing.rest.reactive.ReactiveTypeError; -import fr.inra.oresing.rest.reactive.ReactiveTypeResult; +import fr.inra.oresing.rest.model.data.DownloadDatasetQuery; import fr.inra.oresing.rest.validationcheckresults.ValidationCheckResult; import jakarta.servlet.http.Cookie; +import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.assertj.core.api.Assert; import org.hamcrest.CoreMatchers; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -33,13 +35,8 @@ import org.hamcrest.Matchers; import org.hamcrest.core.Is; import org.hamcrest.core.IsEqual; import org.hamcrest.core.IsNull; -import org.jetbrains.annotations.NotNull; import org.json.JSONArray; -import org.junit.Assert; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -73,7 +70,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -87,6 +83,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @Slf4j public class OreSiResourcesTest { + @Autowired + RelationalService relationalService; + @Autowired private ObjectMapper objectMapper; @Autowired @@ -205,13 +204,21 @@ public class OreSiResourcesTest { MockMultipartFile configuration = new MockMultipartFile("file", "monsore.yaml", "text/plain", in); // on n'a pas le droit de creer de nouvelle application - List<ReactiveTypeError> errors = fixtures.getErrors(fixtures.loadApplication(configuration, monsoreCookie, "monsore", "")); - assertEquals("NO_RIGHT_FOR_APPLICATION_CREATION", ((Map)errors.get(0).result()).get("message")); - assertEquals("monsore", ((Map)errors.get(0).result()).get("applicationName")); + final NotApplicationCreatorRightsException resolvedException = + (NotApplicationCreatorRightsException) fixtures.loadApplicationWithError( + configuration, + monsoreCookie, + "monsore" + ); + addUserRightCreateApplication(monsoreUserId, "monsore"); + assert resolvedException != null; + Assertions.assertEquals("monsore", resolvedException.getApplicationName()); addUserRightCreateApplication(monsoreUserId, "monsore"); MvcResult resultApplication = fixtures.loadApplication(configuration, monsoreCookie, "monsore", "commentaire"); appId = fixtures.getIdFromApplicationResult(resultApplication); + } catch (Throwable e) { + throw new RuntimeException(e); } String response = mockMvc.perform(get("/api/v1/applications/{appId}", appId) @@ -226,9 +233,9 @@ public class OreSiResourcesTest { ApplicationResult applicationResult = objectMapper.readValue(response, ApplicationResult.class); - assertEquals("commentaire", applicationResult.comment()); - assertEquals("monsore", applicationResult.name()); - assertEquals(Set.of("especes", "projet", "sites", "themes", "type_de_fichiers", "type_de_sites", "site_theme_datatype", "unites", "valeurs_qualitatives", "variables", "variables_et_unites_par_types_de_donnees"), applicationResult.references().keySet()); + Assertions.assertEquals("commentaire", applicationResult.comment()); + Assertions.assertEquals("monsore", applicationResult.name()); + Assertions.assertEquals(Set.of("especes", "projet", "sites", "themes", "type_de_fichiers", "type_de_sites", "site_theme_datatype", "unites", "valeurs_qualitatives", "variables", "variables_et_unites_par_types_de_donnees"), applicationResult.references().keySet()); // assertEquals(List.of("pem"), applicationResult.getDataType()); // Ajout de referentiel @@ -314,7 +321,7 @@ public class OreSiResourcesTest { .lines().collect(Collectors.toSet()); Set<String> actual = new String(result.getResponse().getContentAsByteArray()) .lines().collect(Collectors.toSet()); - Assert.assertEquals("Bas site.csv ", expected, actual); + Assertions.assertEquals(expected, actual, "Bas site.csv "); }); } @@ -384,7 +391,7 @@ public class OreSiResourcesTest { String actualJson = getPemData(monsoreCookie); log.debug(StringUtils.abbreviate(actualJson, 50)); } - String contentAsString = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + String contentAsString = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(monsoreCookie)) .andExpect(jsonPath("$.rows[*].values" + "[?(@.projet.value=='projet_atlantique' )]" + @@ -413,7 +420,7 @@ public class OreSiResourcesTest { \"%s\" """, s)) .collect(Collectors.joining(","))); - mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .param("downloadDatasetQuery", query) .cookie(monsoreCookie)) .andExpect(status().is2xxSuccessful()) @@ -467,24 +474,15 @@ public class OreSiResourcesTest { */ String filter = """ { - "application": null, - "applicationNameOrId": null, - "dataType": null, - "offset": null, - "limit": 15, - "variableComponentSelects": [], "variableComponentFilters": [ { "variableComponentKey": { "variable": "date", "component": "value" }, - "filter": null, - "type": "date", - "format": "dd/MM/yyyy", "intervalValues": { - "from": "1984-01-01", - "to": "1984-01-02" + "from": "01/01/1984", + "to": "02/01/1984" } }, { @@ -492,9 +490,6 @@ public class OreSiResourcesTest { "variable": "Nombre d'individus", "component": "value" }, - "filter": null, - "type": "numeric", - "format": "integer", "intervalValues": { "from": 20, "to": 29 @@ -505,10 +500,7 @@ public class OreSiResourcesTest { "variable": "Couleur des individus", "component": "value" }, - "filter": "vert", - "type": "reference", - "format": "uuid", - "intervalValues": null + "filter": "couleur_des_individus__vert" } ], "variableComponentOrderBy": [ @@ -525,16 +517,14 @@ public class OreSiResourcesTest { }"""; { Resources.toString(Objects.requireNonNull(getClass().getResource("/data/monsore/compare/export.json")), Charsets.UTF_8); - String actualJson = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + String actualJson = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(monsoreCookie) - .param("downloadDatasetQuery", filter) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.rows", Matchers.hasSize(8))) - .andExpect(jsonPath("$.rows[*].values.date[?(@.value == 'date:1984-01-01T00:00:00:dd/MM/yyyy')]", Matchers.hasSize(8))) - .andExpect(jsonPath("$.rows[*].values['Nombre d\\'individus'][?(@.value ==25)]", Matchers.hasSize(8))) - .andExpect(jsonPath("$.rows[*].values['Couleur des individus'][?(@.value =='couleur_des_individus__vert')]", Matchers.hasSize(8))) - .andExpect(jsonPath("$.rows[*].values.site.plateforme").value(Stream.of("a", "p1", "p1", "p1", "p1", "p1", "p2", "p2").collect(Collectors.toList()))) + .andExpect(jsonPath("$.rows", Matchers.hasSize(272))) + .andExpect(jsonPath("$.rows[*].values.date[?(@.value == 'date:1984-01-01T00:00:00:dd/MM/yyyy')]", Matchers.hasSize(56))) + .andExpect(jsonPath("$.rows[*].values['Nombre d\\'individus'][?(@.value ==25)]", Matchers.hasSize(32))) + .andExpect(jsonPath("$.rows[*].values['Couleur des individus'][?(@.value =='couleur_des_individus__vert')]", Matchers.hasSize(96))) .andReturn().getResponse().getContentAsString(); log.debug(StringUtils.abbreviate(actualJson, 50)); @@ -571,16 +561,16 @@ public class OreSiResourcesTest { .andExpect(status().is4xxClientError()) .andReturn().getResponse().getContentAsString(); log.debug(StringUtils.abbreviate(response, 50)); - assertTrue(response.contains("projet_manch")); - assertTrue(response.contains("projet_atlantiqu")); + Assertions.assertTrue(response.contains("projet_manch")); + Assertions.assertTrue(response.contains("projet_atlantiqu")); String finalResponse = response; - assertTrue(() -> finalResponse.contains("41"), "Il faut mentionner les lignes en erreur"); - assertTrue(() -> finalResponse.contains("42"), "Il faut mentionner les lignes en erreur"); - assertTrue(() -> finalResponse.contains("43"), "Il faut mentionner les lignes en erreur"); - assertTrue(() -> finalResponse.contains("10"), "Il faut mentionner les lignes en erreur"); - assertFalse(() -> finalResponse.contains("141"), "L'erreur doit être tronquée"); - assertFalse(() -> finalResponse.contains("142"), "L'erreur doit être tronquée"); - assertFalse(() -> finalResponse.contains("143"), "L'erreur doit être tronquée"); + Assertions.assertTrue(() -> finalResponse.contains("41"), "Il faut mentionner les lignes en erreur"); + Assertions.assertTrue(() -> finalResponse.contains("42"), "Il faut mentionner les lignes en erreur"); + Assertions.assertTrue(() -> finalResponse.contains("43"), "Il faut mentionner les lignes en erreur"); + Assertions.assertTrue(() -> finalResponse.contains("10"), "Il faut mentionner les lignes en erreur"); + Assertions.assertFalse(() -> finalResponse.contains("141"), "L'erreur doit être tronquée"); + Assertions.assertFalse(() -> finalResponse.contains("142"), "L'erreur doit être tronquée"); + Assertions.assertFalse(() -> finalResponse.contains("143"), "L'erreur doit être tronquée"); } final String getMonsoere = mockMvc.perform(get("/api/v1/applications/monsore") .cookie(authCookie) @@ -601,7 +591,7 @@ public class OreSiResourcesTest { .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()) .andReturn().getResponse().getContentAsString(); - final String getPem = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + final String getPem = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(monsoreCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()) @@ -613,9 +603,49 @@ public class OreSiResourcesTest { .andDo(result -> { final String[] uuids = result.getResponse().getContentAsString().split(","); int expectedUUIDs = 24; - assertEquals(uuids.length, expectedUUIDs, String.format("On attend %d lignes; la requête en renvoie %d", expectedUUIDs, uuids.length)); + Assertions.assertEquals(uuids.length, expectedUUIDs, String.format("On attend %d lignes; la requête en renvoie %d", expectedUUIDs, uuids.length)); }); + // fix issue #165 + String filterPattern = """ + { + "offset": null, + "limit": 10, + "variableComponentSelects": [], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "filter": %1$s, + "type": "numeric", + "format": "integer", + "intervalValues": %2$s, + "isRegExp": null + } + ], + "variableComponentOrderBy": [], + "authorizationDescriptions": [] + } + """; + /*filter = filterPattern.formatted("\"15\"", null); + mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") + .cookie(monsoreCookie) + .param("downloadDatasetQuery", filter) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.rows", Matchers.not(Matchers.empty())));*/ + + filter = filterPattern.formatted(null, "{\"from\":\"15\",\"to\":\"15\"}"); + mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") + .cookie(monsoreCookie) + .param("downloadDatasetQuery", filter) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.rows", Matchers.not(Matchers.empty()))); + + String adminRights = getJsonRightsforMonSoererepository(withRigthsUserId, OperationType.admin.name(), "pem", @@ -645,7 +675,7 @@ public class OreSiResourcesTest { } private String getPemData(Cookie monsoreCookie) throws Exception { - return mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + return mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(monsoreCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -681,12 +711,16 @@ public class OreSiResourcesTest { //définition de l'application addUserRightCreateApplication(authUserId, "multiplicity"); - String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "multiplicity", "")); + String id = fixtures.getIdFromApplicationResult( + fixtures.loadApplication(configuration, authCookie, "multiplicity", "") + ); mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/multiplicity", "ALL,ReferenceType") .cookie(authCookie) .param("filter", "ALL")) .andExpect(status().is2xxSuccessful()); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel for (Map.Entry<String, String> e : fixtures.getMultiplicityReferencesFiles().entrySet()) { @@ -696,6 +730,9 @@ public class OreSiResourcesTest { String response = mockMvc.perform(multipart("/api/v1/applications/multiplicity/references/{refType}", e.getKey()) .file(refFile) .cookie(authCookie)) + .andDo(result -> { + System.out.println(result); + }) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id", IsNull.notNullValue())) .andReturn().getResponse().getContentAsString(); @@ -725,21 +762,23 @@ public class OreSiResourcesTest { .andExpect(status().is2xxSuccessful()) .andReturn().getResponse().getContentAsString(); - response = mockMvc.perform(get("/api/v1/applications/multiplicity/data/bugs") + response = mockMvc.perform(get("/api/v1/applications/multiplicity/data/bugs/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_reference1.fieldType.fieldType.referenceValues", Matchers.hasKey("tutu__tutu1"))) - .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_reference1.fieldType.fieldType.referenceValues", Matchers.hasKey("tutu__tutu2"))) - .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_reference1.fieldType.fieldType.referenceValues", Matchers.hasKey("toto__toto1"))) - .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_reference1.fieldType.fieldType.referenceValues", Matchers.hasKey("toto__toto2"))) + .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_references.fieldType.fieldType.referenceValues", Matchers.hasKey("tutu__tutu1"))) + .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_references.fieldType.fieldType.referenceValues", Matchers.hasKey("tutu__tutu2"))) + .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_references.fieldType.fieldType.referenceValues", Matchers.hasKey("toto__toto1"))) + .andExpect(jsonPath("$.checkedFormatVariableComponents.ReferenceType.bug_references.fieldType.fieldType.referenceValues", Matchers.hasKey("toto__toto2"))) .andExpect(jsonPath("$.rows[0].values.bug.dates", Matchers.hasItems("date:2002-01-23T00:00:00:dd/MM/yyyy", "date:2002-01-24T00:00:00:dd/MM/yyyy"))) .andExpect(jsonPath("$.rows[0].values.bug.projets", Matchers.hasItems(1, 2))) .andExpect(jsonPath("$.rows[0].values.bug.fichiers", Matchers.hasItems("file1", "file2"))) .andExpect(jsonPath("$.rows[0].values.bug.durations", Matchers.hasItems(3.2, 5.4))) - .andExpect(jsonPath("$.rows[0].values.bug.reference1", Matchers.hasItems("toto__toto1", "tutu__tutu1"))) + .andExpect(jsonPath("$.rows[0].values.bug.references", Matchers.hasItems("toto__toto1", "tutu__tutu1"))) .andReturn().getResponse().getContentAsString(); } + relationalService.createViews("multiplicity", ViewStrategy.VIEW); + System.out.println("fin"); } @Test @@ -753,6 +792,8 @@ public class OreSiResourcesTest { addUserRightCreateApplication(authUserId, "minautor"); String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "minautor", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } // Ajout de referentiel for (Map.Entry<String, String> e : fixtures.getApplicationWithComputedComponentsWithReferencesReferences().entrySet()) { @@ -776,7 +817,7 @@ public class OreSiResourcesTest { .andExpect(status().isCreated()); } } - mockMvc.perform(get("/api/v1/applications/minautor/data/dataset") + mockMvc.perform(get("/api/v1/applications/minautor/data/dataset/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.rows[*].values.informations.site", Matchers.hasSize(7))) @@ -811,14 +852,13 @@ public class OreSiResourcesTest { registerFile("ui/cypress/fixtures/applications/ore/monsore/validateMonsore.txt", responseForTestingmonsoere); registerFile("ui/cypress/fixtures/applications/ore/monsore/createMonsore.txt", responseForCreatemonsoere); registerFile("ui/cypress/fixtures/applications/ore/monsore/changeMonsore.txt", responseForChangemonsoere); - assertTrue(Arrays.stream( - getApplicationsFlux(authCookie, "ALL") + Assertions.assertEquals(Arrays.stream(getApplicationsFlux(authCookie, "ALL") ) - .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) - .filter(s -> JsonPath.parse(s).read("$.result.referenceType", List.class).contains("sites")) - .filter(s -> !JsonPath.parse(s).read("$.result.referenceType", List.class).contains("type de fichiers")) - .count() == 1); + .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) + .filter(s -> JsonPath.parse(s).read("$.result.referenceType", List.class).contains("sites")) + .filter(s -> !JsonPath.parse(s).read("$.result.referenceType", List.class).contains("type de fichiers")) + .count(), 1); mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/monsore") .cookie(authCookie) .param("filter", "ALL")) @@ -846,6 +886,8 @@ public class OreSiResourcesTest { .andExpect(jsonPath("$.dataTypes.pem.variables.site.components.site_bassin.tags", Matchers.hasItem("__hidden__"))) .andExpect(jsonPath("$.dataTypes.test.tags", Matchers.hasItem("__hidden__"))) .andExpect(jsonPath("$.internationalization.internationalizedTags.context.fr", Is.is("contexte"))); + } catch (Throwable e) { + throw new RuntimeException(e); } CreateUserResult withRightsUserResult = authenticationService.createUser("withrigths", "xxxxxxxx", "withrigths@inrae.fr"); setToActive(withRightsUserResult.getUserId()); @@ -1051,18 +1093,18 @@ public class OreSiResourcesTest { String[] read1 = JsonPath.parse(contentAsString).read("$.referenceValues[*].naturalKey", String[].class); naturalKeys.addAll(Arrays.asList(read1)); System.out.println(naturalKeys); - assertEquals("bassin_versant", naturalKeys.get(0)); - assertEquals("plateforme", naturalKeys.get(1)); + Assertions.assertEquals("bassin_versant", naturalKeys.get(0)); + Assertions.assertEquals("plateforme", naturalKeys.get(1)); read1 = JsonPath.parse(contentAsString).read("$.referenceValues[*].hierarchicalKey", String[].class); hierarchicalKeys.addAll(Arrays.asList(read1)); System.out.println(hierarchicalKeys); - assertEquals("bassin_versant", hierarchicalKeys.get(0)); - assertEquals("plateforme", hierarchicalKeys.get(1)); + Assertions.assertEquals("bassin_versant", hierarchicalKeys.get(0)); + Assertions.assertEquals("plateforme", hierarchicalKeys.get(1)); read1 = JsonPath.parse(contentAsString).read("$.referenceValues[*].id", String[].class); ids.addAll(Arrays.asList(read1)); - assertEquals(2, read1.length); + Assertions.assertEquals(2, read1.length); }); // recherche sur un critère mockMvc.perform(MockMvcRequestBuilders.get("/api/v1/applications/monsore/references/{refType}", "type_de_sites") @@ -1089,14 +1131,14 @@ public class OreSiResourcesTest { .param("_row_id_", ids.get(1)) .cookie(withRigthsCookie)) .andReturn().getResponse().getContentAsString(); - assertEquals(ids.get(1), deletedIds); + Assertions.assertEquals(ids.get(1), deletedIds); //suppression par id deletedIds = mockMvc.perform(delete("/api/v1/applications/monsore/references/{refType}", "type_de_sites") .param("_row_id_", ids.get(1)) .cookie(withRigthsCookie)) .andReturn().getResponse().getContentAsString(); - assertEquals("", deletedIds); + Assertions.assertEquals("", deletedIds); // Ajout de referentiel for (Map.Entry<String, String> e : fixtures.getMonsoreReferentielFiles().entrySet()) { @@ -1188,7 +1230,7 @@ public class OreSiResourcesTest { .cookie(lambdaCookie)) .andExpect(status().is4xxClientError()) .andReturn().getResolvedException()).getMessage(); - assertEquals("application inconnue 'monsore'", error); + Assertions.assertEquals("application inconnue 'monsore'", error); //pas de droits mockMvc.perform(get("/api/v1/applications/monsore/additionalFiles") .param("nameOrId", "monsore") @@ -1196,11 +1238,11 @@ public class OreSiResourcesTest { .cookie(lambdaCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(result -> { - Assert.assertTrue("empty data expected", result.getResponse().getContentAsByteArray().length < 40); + Assertions.assertTrue(result.getResponse().getContentAsByteArray().length < 40, "empty data expected"); }); - assertEquals("application inconnue 'monsore'", error); + Assertions.assertEquals("application inconnue 'monsore'", error); //avec droits mockMvc.perform(asyncDispatch(mockMvc.perform(get("/api/v1/applications/monsore/additionalFiles") .param("nameOrId", "monsore") @@ -1228,8 +1270,8 @@ public class OreSiResourcesTest { final List<String> entryNames = entries.stream() .map(ZipEntry::getName) .collect(Collectors.toList()); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); }); ; mockMvc.perform(asyncDispatch(mockMvc.perform(get("/api/v1/applications/monsore/additionalFiles") @@ -1269,8 +1311,8 @@ public class OreSiResourcesTest { .map(ZipEntry::getName) .collect(Collectors.toList()); System.out.println(entryNames); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); }); mockMvc.perform(asyncDispatch(mockMvc.perform(get("/api/v1/applications/monsore/additionalFiles") .param("nameOrId", "monsore") @@ -1307,8 +1349,8 @@ public class OreSiResourcesTest { final List<String> entryNames = entries.stream() .map(ZipEntry::getName) .collect(Collectors.toList()); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); - assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere_infos.txt"), String.format("Le zip doit contenir %s", "monsoere_infos.txt")); + Assertions.assertTrue(() -> entryNames.contains("fichiers/monsoere/monsoere.yaml"), String.format("Le zip doit contenir %s", "monsoere.yaml")); }); ; @@ -1318,9 +1360,10 @@ public class OreSiResourcesTest { .cookie(authCookie)) .andExpect(jsonPath("$.referenceValues", Matchers.hasSize(0))); - final ResultActions dataTest = mockMvc.perform(get("/api/v1/applications/monsore/data/{dataType}", "test") + final ResultActions dataTest = mockMvc.perform(get("/api/v1/applications/monsore/data/{dataType}/json", "test") .cookie(authCookie)) - .andExpect(jsonPath("$.variables", Matchers.hasSize(0))); + .andExpect(jsonPath("$.variables", Matchers.hasSize(0))) + .andExpect(jsonPath("$.totalRows", Matchers.is(-1))); // ajout de data String projet = "manche"; @@ -1371,7 +1414,7 @@ public class OreSiResourcesTest { oirFilesUUID = JsonPath.parse(response).read("$[2].id"); // on vérifie l'absence de data - response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.totalRows").value(-1)) @@ -1386,10 +1429,10 @@ public class OreSiResourcesTest { .andExpect(status().is4xxClientError()) .andReturn().getResolvedException(); - assertTrue(exception instanceof SiOreIllegalArgumentException); - assertEquals("noRightForPublish", exception.getMessage()); - assertEquals("pem", ((SiOreIllegalArgumentException) exception).getParams().get("dataType")); - assertEquals("monsore", ((SiOreIllegalArgumentException) exception).getParams().get("application")); + Assertions.assertTrue(exception instanceof SiOreIllegalArgumentException); + Assertions.assertEquals("noRightForPublish", exception.getMessage()); + Assertions.assertEquals("pem", ((SiOreIllegalArgumentException) exception).getParams().get("dataType")); + Assertions.assertEquals("monsore", ((SiOreIllegalArgumentException) exception).getParams().get("application")); // on donne les droits publication @@ -1423,7 +1466,7 @@ public class OreSiResourcesTest { // on récupère le data en base - response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.totalRows").value(34)) @@ -1469,8 +1512,8 @@ public class OreSiResourcesTest { publishOrDepublish(withRigthsCookie, "manche", "plateforme", "nivelle", 34, true, 1, true); } catch (SiOreIllegalArgumentException e) { - assertEquals("noRightOnTable", e.getMessage()); - assertEquals("data", e.getParams().get("table")); + Assertions.assertEquals("noRightOnTable", e.getMessage()); + Assertions.assertEquals("data", e.getParams().get("table")); } final String createRights = getJsonRightsforMonSoererepository(withRigthsUserId, OperationType.publication.name(), "pem", "plateforme.nivelle.nivelle__p1", "1984,1,1", "1984,1,6", authCookie); @@ -1504,7 +1547,7 @@ public class OreSiResourcesTest { // on récupère le data en base si j'ai les droits de publication je peux aussi lire les données avec ces droits - response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(withRigthsCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.rows[*].values.site[?(@.chemin==\"plateforme.nivelle.nivelle__p1\")].chemin", Matchers.hasSize(34))) @@ -1515,10 +1558,6 @@ public class OreSiResourcesTest { .andReturn().getResponse().getContentAsString(); String filter = """ { - "application": null, - "applicationNameOrId": null, - "dataType": null, - "offset": null, "limit": 15, "authorizationDescriptions": [ { @@ -1533,7 +1572,7 @@ public class OreSiResourcesTest { ] }"""; - mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .param("downloadDatasetQuery", filter) .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) @@ -1541,7 +1580,7 @@ public class OreSiResourcesTest { .andExpect(jsonPath("$.rows[*].values.site[?(@.chemin=='plateforme.scarff.scarff__p1')]", hasSize(14))) .andExpect(jsonPath("$.rows[*].values.date[?(@.value=='date:1984-01-01T00:00:00:dd/MM/yyyy')]", hasSize(14))); - response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem") + response = mockMvc.perform(get("/api/v1/applications/monsore/data/pem/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.rows[*].values.site[?(@.chemin==\"plateforme.scarff.scarff__p1\")].chemin", Matchers.hasSize(68))) @@ -1560,15 +1599,15 @@ public class OreSiResourcesTest { .andReturn() .getResolvedException(); assert resolvedException != null; - assertEquals("NO_RIGHT_FOR_DELETE_RIGHTS_APPLICATION", resolvedException.getMessage()); - assertEquals("pem", resolvedException.getDataType()); - assertEquals("monsore", resolvedException.getApplicationName()); + Assertions.assertEquals("NO_RIGHT_FOR_DELETE_RIGHTS_APPLICATION", resolvedException.getMessage()); + Assertions.assertEquals("pem", resolvedException.getDataType()); + Assertions.assertEquals("monsore", resolvedException.getApplicationName()); final Exception resolvedException1 = mockMvc.perform(delete("/api/v1/applications/monsore/data/pem") .cookie(authCookie)) .andExpect(status().is4xxClientError()) .andReturn() .getResolvedException(); - assertTrue(resolvedException1 instanceof DeleteOnrepositoryApplicationNotAllowedException); + Assertions.assertTrue(resolvedException1 instanceof DeleteOnrepositoryApplicationNotAllowedException); //on donne les droits de suppression final String deleteRights = getJsonRightsforMonSoererepository(withRigthsUserId, OperationType.delete.name(), "pem", "plateforme.nivelle.nivelle__p1", "1984,1,1", "1984,1,6", authCookie); @@ -1585,7 +1624,7 @@ public class OreSiResourcesTest { .param("params", additionalJsonRequest) .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) - .andExpect(result -> assertEquals(additionalfileUUID.replace("\"", ""), result.getResponse().getContentAsString())); + .andExpect(result -> Assertions.assertEquals(additionalfileUUID.replace("\"", ""), result.getResponse().getContentAsString())); // on supprime l'authorization' @@ -1617,6 +1656,8 @@ public class OreSiResourcesTest { //définition de l'application addUserRightCreateApplication(authUserId, "teledec"); String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "teledec", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } fixtures.getTeledetectionReferencesFiles() .forEach((refName, refPath) -> { @@ -1647,10 +1688,10 @@ public class OreSiResourcesTest { .add(s); } } - assertEquals(7, expected.get("ndvi_s2_max_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_max_10m")); - assertEquals(7, expected.get("ndvi_s2_mean_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_mean_10m")); - assertEquals(7, expected.get("ndvi_s2_min_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_min_10m")); - assertEquals(7, expected.get("ndvi_s2_sd_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_sd_10m")); + Assertions.assertEquals(7, expected.get("ndvi_s2_max_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_max_10m")); + Assertions.assertEquals(7, expected.get("ndvi_s2_mean_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_mean_10m")); + Assertions.assertEquals(7, expected.get("ndvi_s2_min_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_min_10m")); + Assertions.assertEquals(7, expected.get("ndvi_s2_sd_10m").size(), () -> "expected %s in %s ".formatted(3, "ndvi_s2_sd_10m")); return true; } @@ -1857,6 +1898,8 @@ public class OreSiResourcesTest { //définition de l'application addUserRightCreateApplication(authUserId, "progressive"); String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "progressive", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } //pas de referentiels progressiveYamlAddData(); @@ -1870,15 +1913,15 @@ public class OreSiResourcesTest { { - assertTrue(Arrays.stream(getApplicationsFlux(readerCookies, "ALL")) + Assertions.assertEquals(Arrays.stream(getApplicationsFlux(readerCookies, "ALL")) - .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) - .filter(s -> JsonPath.parse(s).read("$.result.name", String.class).equals("progressive")) - .count() == 1); + .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) + .filter(s -> JsonPath.parse(s).read("$.result.name", String.class).equals("progressive")) + .count(), 1); } { - mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite") + mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite/json") .cookie(readerCookies) .accept(MediaType.TEXT_PLAIN)) .andExpect(status().is4xxClientError()); @@ -1922,15 +1965,15 @@ public class OreSiResourcesTest { { // Une fois l'accès donné, on doit pouvoir avec l'application dans la liste" - assertTrue(Arrays.stream(getApplicationsFlux(readerCookies, "ALL")) + Assertions.assertEquals(Arrays.stream(getApplicationsFlux(readerCookies, "ALL")) - .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) - .filter(s -> JsonPath.parse(s).read("$.result.name", String.class).equals("progressive")) - .count() == 1); + .filter(s -> JsonPath.parse(s).read("$.type", String.class).equals("REACTIVE_RESULT")) + .filter(s -> JsonPath.parse(s).read("$.result.name", String.class).equals("progressive")) + .count(), 1); } { - String json = mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite") + String json = mockMvc.perform(get("/api/v1/applications/progressive/data/date_de_visite/json") .cookie(readerCookies) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -1960,6 +2003,8 @@ public class OreSiResourcesTest { addUserRightCreateApplication(authUserId, "progressive"); String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "progressive", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } progressiveYamlAddReferences(); @@ -1979,14 +2024,18 @@ public class OreSiResourcesTest { MockMultipartFile configuration = new MockMultipartFile("file", "progressive.yaml", "text/plain", in); //définition de l'application addUserRightCreateApplication(authUserId, "progressive"); - List<ReactiveTypeError> errors = fixtures.getErrors(fixtures.loadApplication(configuration, authCookie, "progressive", "")); - final Map validationCheckResult = (Map) ((List) ((LinkedHashMap) errors.get(0).result()).get("validationCheckResults")).get(0); - assertEquals("authorizationScopeMissingReferenceCheckerForAuthorizationScope", validationCheckResult.get("message")); - final Map<String, Object> messageParams = (Map<String, Object>) validationCheckResult.get("messageParams"); - assertEquals("localization", messageParams.get("authorizationScopeName")); - assertEquals("date_de_visite", messageParams.get("dataType")); - assertEquals("agroecosysteme", messageParams.get("component")); - assertEquals("localisation", messageParams.get("variable")); + + BadApplicationConfigurationException exception = (BadApplicationConfigurationException) + fixtures.loadApplicationWithError(configuration, authCookie, "progressive"); + assert exception != null; + final ValidationCheckResult validationCheckResult = exception.getConfigurationParsingResult().validationCheckResults() + .get(0); + Assertions.assertEquals("authorizationScopeMissingReferenceCheckerForAuthorizationScope", validationCheckResult.message()); + final Map<String, Object> messageParams = validationCheckResult.messageParams(); + Assertions.assertEquals("localization", messageParams.get("authorizationScopeName")); + Assertions.assertEquals("date_de_visite", messageParams.get("dataType")); + Assertions.assertEquals("agroecosysteme", messageParams.get("component")); + Assertions.assertEquals("localisation", messageParams.get("variable")); } } @@ -2003,6 +2052,8 @@ public class OreSiResourcesTest { //pas de référentiel progressiveYamlAddData(); + } catch (Throwable e) { + throw new RuntimeException(e); } } @@ -2020,6 +2071,8 @@ public class OreSiResourcesTest { progressiveYamlAddReferences(); progressiveYamlAddData(); + } catch (Throwable e) { + throw new RuntimeException(e); } } @@ -2039,6 +2092,8 @@ public class OreSiResourcesTest { String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "progressive", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } progressiveYamlAddReferences(); progressiveYamlAddData(); @@ -2097,6 +2152,8 @@ public class OreSiResourcesTest { .andExpect(jsonPath("$.internationalization.references.taxon.internationalizedDynamicColumns['propriétés de taxons'].en", IsEqual.equalTo("Properties of Taxa"))) .andReturn().getResponse().getContentAsString(); + } catch (Throwable e) { + throw new RuntimeException(e); } String response; @@ -2175,6 +2232,8 @@ public class OreSiResourcesTest { try (InputStream in = resource.openStream()) { MockMultipartFile configuration = new MockMultipartFile("file", "acbb.yaml", "text/plain", in); String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "acbb", "")); + } catch (Throwable e) { + throw new RuntimeException(e); } addReferences(); @@ -2210,14 +2269,14 @@ public class OreSiResourcesTest { } { - String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/SWC") + String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/SWC/json") .cookie(authCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); log.debug(StringUtils.abbreviate(actualJson, 50)); - assertEquals(2912, StringUtils.countMatches(actualJson, "\"SWC\":")); + Assertions.assertEquals(2912, StringUtils.countMatches(actualJson, "\"SWC\":")); } { @@ -2249,14 +2308,14 @@ public class OreSiResourcesTest { } { - String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur") + String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/biomasse_production_teneur/json") .cookie(authCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()) .andReturn().getResponse().getContentAsString(); log.debug(StringUtils.abbreviate(actualJson, 50)); - assertEquals(252, StringUtils.countMatches(actualJson, "prairie permanente")); + Assertions.assertEquals(252, StringUtils.countMatches(actualJson, "prairie permanente")); } { @@ -2287,7 +2346,7 @@ public class OreSiResourcesTest { // restitution de data json { // String expectedJson = Resources.toString(getClass().getResource("/data/acbb/compare/export.json"), Charsets.UTF_8); - String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/flux_tours") + String actualJson = mockMvc.perform(get("/api/v1/applications/acbb/data/flux_tours/json") .cookie(authCookie) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -2325,13 +2384,13 @@ public class OreSiResourcesTest { try (ZipInputStream zi = new ZipInputStream(new ByteArrayInputStream(contentAsByteArray))) { ZipEntry entry = zi.getNextEntry(); while (entry != null) { - assertNotNull(entry, "l'entrée est nulle "); + Assertions.assertNotNull(entry, "l'entrée est nulle "); findedEntries.add(entry.getName()); entry = zi.getNextEntry(); } expectedEntries .forEach(e -> - assertTrue(() -> findedEntries.contains(e), String.format("Le zip doit contenir %s", e))); + Assertions.assertTrue(() -> findedEntries.contains(e), String.format("Le zip doit contenir %s", e))); } catch (IOException e) { throw new RuntimeException(e); } @@ -2375,7 +2434,7 @@ public class OreSiResourcesTest { @Test @Tag("HAUTE_FREQUENCE_TEST") - public void addApplicationHauteFrequence() throws Exception { + public void addApplicationHauteFrequence() throws Throwable { addUserRightCreateApplication(authUserId, "hautefrequence"); try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getHauteFrequenceApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "hautefrequence.yaml", "text/plain", configurationFile); @@ -2405,7 +2464,7 @@ public class OreSiResourcesTest { @Test @Tag("OTHERS_TEST") - public void addDuplicatedTest() throws Exception { + public void addDuplicatedTest() throws Throwable { addUserRightCreateApplication(authUserId, "duplicated"); try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getDuplicatedApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "duplicated.yaml", "text/plain", configurationFile); @@ -2460,19 +2519,19 @@ public class OreSiResourcesTest { .cookie(authCookie)); //fail(); } catch (NestedServletException e) { - assertTrue(e.getCause() instanceof InvalidDatasetContentException); + Assertions.assertTrue(e.getCause() instanceof InvalidDatasetContentException); final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); - assertEquals(1, errors.size()); - assertEquals(4, errors.get(0).lineNumber()); + Assertions.assertEquals(1, errors.size()); + Assertions.assertEquals(4, errors.get(0).lineNumber()); final ValidationCheckResult validationCheckResult = errors.get(0).validationCheckResult(); - assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); - assertEquals("duplicatedLineInDatatype", validationCheckResult.message()); + Assertions.assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); + Assertions.assertEquals("duplicatedLineInDatatype", validationCheckResult.message()); final Map<String, Object> messageParams = validationCheckResult.messageParams(); - assertEquals("types_de_zones_etudes", messageParams.get("file")); - assertEquals(4, messageParams.get("lineNumber")); - assertArrayEquals(new Integer[]{3, 4}, ((Set) messageParams.get("otherLines")).toArray()); - assertEquals("zone20", messageParams.get("duplicateKey")); + Assertions.assertEquals("types_de_zones_etudes", messageParams.get("file")); + Assertions.assertEquals(4, messageParams.get("lineNumber")); + Assertions.assertArrayEquals(new Integer[]{3, 4}, ((Set) messageParams.get("otherLines")).toArray()); + Assertions.assertEquals("zone20", messageParams.get("duplicateKey")); } //il doit toujours y avoir le même nombre de ligne @@ -2533,19 +2592,19 @@ on test le dépôt d'un fichier récursif .cookie(authCookie)); //fail(); } catch (NestedServletException e) { - assertTrue(e.getCause() instanceof InvalidDatasetContentException); + Assertions.assertTrue(e.getCause() instanceof InvalidDatasetContentException); final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); - assertEquals(1, errors.size()); - assertEquals(4, errors.get(0).lineNumber()); + Assertions.assertEquals(1, errors.size()); + Assertions.assertEquals(4, errors.get(0).lineNumber()); final ValidationCheckResult validationCheckResult = errors.get(0).validationCheckResult(); - assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); - assertEquals("duplicatedLineInDatatype", validationCheckResult.message()); + Assertions.assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); + Assertions.assertEquals("duplicatedLineInDatatype", validationCheckResult.message()); final Map<String, Object> messageParams = validationCheckResult.messageParams(); - assertEquals("zones_etudes", messageParams.get("file")); - assertEquals(4, messageParams.get("lineNumber")); - assertArrayEquals(new Integer[]{2, 4}, ((Set) messageParams.get("otherLines")).toArray()); - assertEquals("site1", messageParams.get("duplicateKey")); + Assertions.assertEquals("zones_etudes", messageParams.get("file")); + Assertions.assertEquals(4, messageParams.get("lineNumber")); + Assertions.assertArrayEquals(new Integer[]{2, 4}, ((Set) messageParams.get("otherLines")).toArray()); + Assertions.assertEquals("site1", messageParams.get("duplicateKey")); } //il doit toujours y avoir le même nombre de ligne @@ -2565,19 +2624,19 @@ on test le dépôt d'un fichier récursif .cookie(authCookie)); //fail(); } catch (NestedServletException e) { - assertTrue(e.getCause() instanceof InvalidDatasetContentException); + Assertions.assertTrue(e.getCause() instanceof InvalidDatasetContentException); final InvalidDatasetContentException invalidDatasetContentException = (InvalidDatasetContentException) e.getCause(); final List<CsvRowValidationCheckResult> errors = invalidDatasetContentException.getErrors(); - assertEquals(1, errors.size()); - assertEquals(3, errors.get(0).lineNumber()); + Assertions.assertEquals(1, errors.size()); + Assertions.assertEquals(3, errors.get(0).lineNumber()); final ValidationCheckResult validationCheckResult = errors.get(0).validationCheckResult(); - assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); - assertEquals("missingParentLineInRecursiveReference", validationCheckResult.message()); + Assertions.assertEquals(ValidationLevel.ERROR, validationCheckResult.level()); + Assertions.assertEquals("missingParentLineInRecursiveReference", validationCheckResult.message()); final Map<String, Object> messageParams = validationCheckResult.messageParams(); - assertEquals("zones_etudes", messageParams.get("references")); - assertEquals(3L, messageParams.get("lineNumber")); - assertEquals("site3", messageParams.get("missingReferencesKey")); - assertTrue(Set.of("site3", "site1.site2", "site1", "site2").containsAll((Set) messageParams.get("knownReferences"))); + Assertions.assertEquals("zones_etudes", messageParams.get("references")); + Assertions.assertEquals(3L, messageParams.get("lineNumber")); + Assertions.assertEquals("site3", messageParams.get("missingReferencesKey")); + Assertions.assertTrue(Set.of("site3", "site1.site2", "site1", "site2").containsAll((Set) messageParams.get("knownReferences"))); } //il doit toujours y avoir le même nombre de ligne @@ -2598,7 +2657,7 @@ on test le dépôt d'un fichier récursif } //on teste le nombre de ligne try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { - final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty") + final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.totalRows", IsEqual.equalTo(4 @@ -2620,7 +2679,7 @@ on test le dépôt d'un fichier récursif } // le nombre de ligne est inchangé try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { - final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty") + final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.totalRows", IsEqual.equalTo(4 @@ -2645,7 +2704,7 @@ on test le dépôt d'un fichier récursif } // le nombre de ligne est inchangé try (InputStream refStream = fixtures.getClass().getResourceAsStream(dataWithoutDuplicateds)) { - final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty") + final String response = mockMvc.perform(get("/api/v1/applications/duplicated/data/dty/json") .cookie(authCookie)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$.totalRows", IsEqual.equalTo(4 @@ -2661,7 +2720,10 @@ on test le dépôt d'un fichier récursif addUserRightCreateApplication(authUserId, "olac"); try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getOlaApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "olac.yaml", "text/plain", configurationFile); - String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "olac", "")); + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/olac") + .file(configuration) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); } final String contentAsString = mockMvc.perform(get("/api/v1/applications/olac", "ALL,ReferenceType") .cookie(authCookie) @@ -2752,7 +2814,10 @@ on test le dépôt d'un fichier récursif addUserRightCreateApplication(authUserId, "foret"); try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getForetEssaiApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "foret_essai.yaml", "text/plain", configurationFile); - String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "foret", "")); + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret") + .file(configuration) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); } // Ajout de referentiel @@ -2798,9 +2863,10 @@ on test le dépôt d'un fichier récursif addUserRightCreateApplication(authUserId, "foret"); try (InputStream configurationFile = fixtures.getClass().getResourceAsStream(fixtures.getForetApplicationConfigurationResourceName())) { MockMultipartFile configuration = new MockMultipartFile("file", "foret.yaml", "text/plain", configurationFile); - - String id = fixtures.getIdFromApplicationResult(fixtures.loadApplication(configuration, authCookie, "foret", "")); - + mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/foret") + .file(configuration) + .cookie(authCookie)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()); } // Ajout de referentiel diff --git a/src/test/java/fr/inra/oresing/rest/RelationalServiceTest.java b/src/test/java/fr/inra/oresing/rest/RelationalServiceTest.java index 328645960903003f4a5413aab9f17e8acb340e6d..74ffbb6330548b7b9da27c76a15cc65ca2425739 100644 --- a/src/test/java/fr/inra/oresing/rest/RelationalServiceTest.java +++ b/src/test/java/fr/inra/oresing/rest/RelationalServiceTest.java @@ -1,24 +1,31 @@ package fr.inra.oresing.rest; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.google.common.collect.ImmutableSet; import fr.inra.oresing.OreSiNg; +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.persistence.SqlSchemaForRelationalViewsForApplication; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc; import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrint; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; -import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.utility.DockerImageName; +import org.springframework.util.FileCopyUtils; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -28,7 +35,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.testcontainers.utility.DockerImageName.*; @ExtendWith(SpringExtension.class) @SpringBootTest(classes = OreSiNg.class) @@ -107,36 +113,36 @@ public class RelationalServiceTest { { List<Map<String, Object>> viewContent = relationalService.readView("olac", "condition_prelevements", ViewStrategy.VIEW); - assertEquals(19, viewContent.size()); + assertEquals(2169, viewContent.size()); } { List<Map<String, Object>> viewContent = relationalService.readView("olac", "physico-chimie", ViewStrategy.VIEW); - assertEquals(771, viewContent.size()); + assertEquals(2169, viewContent.size()); } { // request.setRequestClient(applicationCreatorRequestClient); List<Map<String, Object>> viewContent = relationalService.readView("acbb", "flux_tours", ViewStrategy.VIEW); - assertEquals(17568, viewContent.size()); + assertEquals(19276, viewContent.size()); } { // request.setRequestClient(applicationCreatorRequestClient); List<Map<String, Object>> viewContent = relationalService.readView("acbb", "biomasse_production_teneur", ViewStrategy.VIEW); - assertEquals(252, viewContent.size()); + assertEquals(19276, viewContent.size()); } { // request.setRequestClient(applicationCreatorRequestClient); List<Map<String, Object>> viewContent = relationalService.readView("acbb", "SWC", ViewStrategy.VIEW); - assertEquals(1456, viewContent.size()); + assertEquals(19276, viewContent.size()); } { // on vérifie juste le bon typage des colonnes (on ne peut moyenne que si la colonne est un nombre) - int averageSwc = namedParameterJdbcTemplate.queryForObject("select avg(swc.\"SWC_valeur\") from acbb_view.swc where swc.\"SWC_valeur\" != -9999", Collections.emptyMap(), Integer.class); + int averageSwc = namedParameterJdbcTemplate.queryForObject("select avg(swc.\"swc_valeur\") from acbb_view.swc where swc.\"swc_valeur\" != -9999", Collections.emptyMap(), Integer.class); assertEquals(26, averageSwc); } diff --git a/src/test/java/fr/inra/oresing/rest/SWAGGER_BUILD.java b/src/test/java/fr/inra/oresing/rest/SWAGGER_BUILD.java deleted file mode 100644 index 41ea945e8ae4b83b73ce95251d279e8b09509c48..0000000000000000000000000000000000000000 --- a/src/test/java/fr/inra/oresing/rest/SWAGGER_BUILD.java +++ /dev/null @@ -1,4 +0,0 @@ -package fr.inra.oresing.rest; - -public class SWAGGER_BUILD { -} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/TestReferencesErrors.java b/src/test/java/fr/inra/oresing/rest/TestReferencesErrors.java index 03966c3475d6f14f69a621316d1fa7ddf9230413..79bc9aa69d8071a055da8eb7bb82f936bd86358c 100644 --- a/src/test/java/fr/inra/oresing/rest/TestReferencesErrors.java +++ b/src/test/java/fr/inra/oresing/rest/TestReferencesErrors.java @@ -136,6 +136,8 @@ public class TestReferencesErrors { .andExpect(jsonPath("$.internationalization.references.taxon.internationalizedDynamicColumns['propriétés de taxons'].en", IsEqual.equalTo("Properties of Taxa"))) .andReturn().getResponse().getContentAsString(); + } catch (Throwable e) { + throw new RuntimeException(e); } String response; @@ -260,6 +262,8 @@ public class TestReferencesErrors { .andExpect(status().is2xxSuccessful()) .andReturn().getResponse().getContentAsString(); + } catch (Throwable e) { + throw new RuntimeException(e); } String response; @@ -330,6 +334,8 @@ public class TestReferencesErrors { .andExpect(status().is2xxSuccessful()) .andReturn().getResponse().getContentAsString(); + } catch (Throwable e) { + throw new RuntimeException(e); } String response; diff --git a/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryAdvancedSearchTest.java b/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryAdvancedSearchTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1db360dfca91bfc9ed014ca3d99253fd06dd05f4 --- /dev/null +++ b/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryAdvancedSearchTest.java @@ -0,0 +1,654 @@ +package fr.inra.oresing.rest.model.data; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.model.VariableComponentKey; +import fr.inra.oresing.model.data.DownloadDatasetQueryAdvancedSearch; +import fr.inra.oresing.model.data.DownloadDatasetQueryByRowId; +import fr.inra.oresing.model.data.DownloadDatasetQuerySimpleSearch; +import fr.inra.oresing.persistence.DataRepository; +import fr.inra.oresing.persistence.JsonRowMapper; +import fr.inra.oresing.persistence.Ltree; +import fr.inra.oresing.rest.exceptions.data.BadDownloadDatasetQuery; +import org.apache.commons.collections.CollectionUtils; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.FileCopyUtils; +import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +@Tag("MODEL_REQUEST_TEST") +class DownloadDatasetQueryAdvancedSearchTest { + + public Fixture fixture = new Fixture(); + public static JsonRowMapper mapper; + public static Set<String> rowIds = Set.of("addf3698-88f2-43f9-8926-0b64a86f3678", "0aef7ed1-1df9-4fbf-a676-1932e87ced9d", "2c527cbe-3ed7-4883-b7d1-8f29eff99eb1"); + + private DownloadDatasetQuery downloadDatasetQueryAdvancedSearch; + private DownloadDatasetQuery downloadDatasetQuerySimpleSearch; + + @BeforeAll + public static void init() { + mapper = new JsonRowMapper<>(); + } + + private static void testFilter(DownloadDatasetQueryAdvancedSearch.VariableComponentFilterSimpleSearch variableComponentFilterSimpleSearch, String filter) { + assertEquals(filter, variableComponentFilterSimpleSearch.filter(), "%s expected".formatted(filter)); + } + + private static void testVariableComponent(DownloadDatasetQueryAdvancedSearch.ForVariableComponent variableComponentFilters, String variable, String component) { + assertEquals(variable, variableComponentFilters.variableComponentKey().variable(), "variable must be %s".formatted(variable)); + assertEquals(component, variableComponentFilters.variableComponentKey().component(), "component must be %s".formatted(component)); + } + + @BeforeEach + public void before() throws IOException { + String simpleSearchJson = """ + { + "application": null, + "applicationNameOrId": "monsores", + "dataType": "pem", + "reference": null, + "offset": null, + "limit": 10, + "variableComponentSelects": [{ + "variable": "Date", + "component": "day" + }, + { + "variable": "Date", + "component": "time" + } + ], + "authorizationDescriptions": [ + { + "timeScope": { + "from": "1984-01-01", + "to": "1984-01-02" + }, + "requiredAuthorizations": [ + { + "projet": "projet_manche", + "localization": "plateforme" + } + ] + }, + { + "timeScope": { + "from": "1984-01-03", + "to": "1984-01-04" + }, + "requiredAuthorizations": [ + { + "projet": "atlantique", + "localization": "plateforme" + } + ] + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + } + """; + + String advancedSearchJson = """ + { + "application": null, + "applicationNameOrId": null, + "dataType": null, + "offset": null, + "limit": 15, + "variableComponentSelects": [{ + "variable": "Date", + "component": "day" + }, + { + "variable": "Date", + "component": "time" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Date", + "component": "day" + }, + "type": "date", + "format": "dd/MM/yyyy", + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + }, + { + "variableComponentKey": { + "variable": "Date", + "component": "time" + }, + "type": "time", + "format": "HH:mm:ss", + "intervalValues": { + "from": "12:20:45", + "to": "16:21:32" + } + }, + { + "variableComponentKey": { + "variable": "Date", + "component": "datetime" + }, + "type": "datetime", + "format": "dd/MM/yyyy HH:mm:ss", + "intervalValues": { + "from": "01/01/1984 12:20:45", + "to": "01/01/1984 16:21:32" + } + }, + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "type": "numeric", + "intervalValues": { + "from": "5", + "to": "40" + }, + "isRegExp": null + }, + { + "variableComponentKey": { + "variable": "date", + "component": "day" + }, + "type": "date", + "filter": "01/01/1984", + "format": "dd/MM/yyyy" + }, + { + "variableComponentKey": { + "variable": "date", + "component": "datetime" + }, + "type": "datetime", + "filter": "01/01/1984 01:12:43", + "format": "dd/MM/yyyy hh:mm:ss" + }, + { + "variableComponentKey": { + "variable": "date", + "component": "time" + }, + "type": "time", + "filter": "01:12:43", + "format": "hh:mm:ss" + }, + { + "variableComponentKey": { + "variable": "individus", + "component": "value" + }, + "type": "numeric", + "filter": "12.3" + }, + { + "variableComponentKey": { + "variable": "site", + "component": "bassin" + }, + "filter": "lebassin" + }, + { + "variableComponentKey": { + "variable": "site", + "component": "projet" + }, + "filter": "l[ae]bassine+", + "intervalValues": null, + "isRegExp": true + }, + { + "variableComponentKey": { + "variable": "projet", + "component": "value" + }, + "filter": "projet_manche", + "type": "reference" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + }"""; + downloadDatasetQueryAdvancedSearch = fixture.addApplication((DownloadDatasetQuery) mapper.toObject(advancedSearchJson, DownloadDatasetQuery.class)); + downloadDatasetQuerySimpleSearch = fixture.addApplication((DownloadDatasetQuery) mapper.toObject(simpleSearchJson, DownloadDatasetQuery.class)); + } + + @Test + void TestFilterForRowIds() throws IOException { + downloadDatasetQueryAdvancedSearch.rowIds = rowIds; + fr.inra.oresing.model.data.DownloadDatasetQuery build = DownloadDatasetQuery.build(fixture.addApplication(downloadDatasetQueryAdvancedSearch)); + assertTrue(build instanceof DownloadDatasetQueryByRowId, "must be an advanced Search"); + DownloadDatasetQueryByRowId downloadDatasetQueryByRowId = (DownloadDatasetQueryByRowId) build; + Set<DownloadDatasetQueryByRowId.DataRowIds> dataRowIds = downloadDatasetQueryByRowId.rowIds(); + Set<DownloadDatasetQueryByRowId.DataRowIds> expected = rowIds.stream() + .map(UUID::fromString) + .map(DownloadDatasetQueryByRowId.DataRowIds::new) + .collect(Collectors.toSet()); + assertEquals(expected, dataRowIds); + + + } + + @Test + @Disabled //TODO + void TestAdvancedSearch() throws IOException { + fr.inra.oresing.model.data.DownloadDatasetQuery build = DownloadDatasetQuery.build(fixture.addApplication(downloadDatasetQueryAdvancedSearch)); + assertTrue(build instanceof DownloadDatasetQueryAdvancedSearch, "must be an advanced Search"); + DownloadDatasetQueryAdvancedSearch advancedSearch = (DownloadDatasetQueryAdvancedSearch) build; + Set<DownloadDatasetQueryAdvancedSearch.VariableComponentFilters> variableComponentFilters = + advancedSearch.variableComponentFilters(); + Set<DownloadDatasetQueryAdvancedSearch.VariableComponentOrderBy> variableComponentOrderBys = + advancedSearch.variableComponentOrderBy(); + Set<VariableComponentKey> variableComponentSelects = + advancedSearch.variableComponentSelects(); + if (!CollectionUtils.isEmpty(variableComponentSelects) && variableComponentSelects.size() == 2) { + variableComponentSelects.stream().map(VariableComponentKey::getId).forEach( + id -> { + List<String> expecteds = List.of("Date_day", "Date_time"); + assertTrue(expecteds.contains(id), "id %s must be in %s".formatted(id, expecteds)); + } + ); + } else { + fail("select must have size 2"); + } + if (!CollectionUtils.isEmpty(variableComponentFilters)) { + if (variableComponentFilters.stream().allMatch(f -> { + switch (f) { + case DownloadDatasetQueryAdvancedSearch.NoVariableComponentFilters noVariableComponentFilters -> { + fail("must be defined"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFilterForInterval variableComponentFilterForInterval -> { + switch (variableComponentFilterForInterval) { + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByNumeric variableComponentFiltersForIntervalByNumeric -> { + testVariableComponent(variableComponentFiltersForIntervalByNumeric, "Nombre d'individus", "value"); + testIntervalValue(variableComponentFiltersForIntervalByNumeric.intervalValues(), "5", "40"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByDate variableComponentFiltersForIntervalByDate -> { + testVariableComponent(variableComponentFiltersForIntervalByDate, "Date", "day"); + testIntervalValue(variableComponentFiltersForIntervalByDate.intervalValues(), "01/01/1984", "04/01/1984"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByTime variableComponentFiltersForIntervalByTime -> { + testVariableComponent(variableComponentFiltersForIntervalByTime, "Date", "time"); + testIntervalValue(variableComponentFiltersForIntervalByTime.intervalValues(), "12:20:45", "16:21:32"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForIntervalByDateTime variableComponentFiltersForIntervalByDateTime -> { + testVariableComponent(variableComponentFiltersForIntervalByDateTime, "Date", "datetime"); + testIntervalValue(variableComponentFiltersForIntervalByDateTime.intervalValues(), "01/01/1984 12:20:45", "01/01/1984 16:21:32"); + } + case null, default -> { + fail("must be defined"); + } + + + } + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFilterSimpleSearch variableComponentFilterSimpleSearch -> { + switch (variableComponentFilterSimpleSearch) { + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByReference variableComponentFiltersByReference -> { + testVariableComponent(variableComponentFiltersByReference, "projet", "value"); + testFilter(variableComponentFiltersByReference, "projet_manche"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByDate variableComponentFiltersByDate -> { + testVariableComponent(variableComponentFiltersByDate, "date", "day"); + testFilter(variableComponentFiltersByDate, "01/01/1984"); + testFormat(variableComponentFiltersByDate, "dd/MM/yyyy"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByTime variableComponentFiltersByTime -> { + testVariableComponent(variableComponentFiltersByTime, "date", "time"); + testFilter(variableComponentFiltersByTime, "01:12:43"); + testFormat(variableComponentFiltersByTime, "hh:mm:ss"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByDateTime variableComponentFiltersByDateTime -> { + testVariableComponent(variableComponentFiltersByDateTime, "date", "datetime"); + testFilter(variableComponentFiltersByDateTime, "01/01/1984 01:12:43"); + testFormat(variableComponentFiltersByDateTime, "dd/MM/yyyy hh:mm:ss"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersByNumeric variableComponentFiltersByNumeric -> { + testVariableComponent(variableComponentFiltersByNumeric, "individus", "value"); + testFilter(variableComponentFiltersByNumeric, "12.3"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByPlainText variableComponentFiltersForWordByPlainText -> { + testVariableComponent(variableComponentFiltersForWordByPlainText, "site", "bassin"); + testFilter(variableComponentFiltersForWordByPlainText, "lebassin"); + } + case DownloadDatasetQueryAdvancedSearch.VariableComponentFiltersForWordByRegexp variableComponentFiltersForWordByRegexp -> { + testVariableComponent(variableComponentFiltersForWordByRegexp, "site", "projet"); + testFilter(variableComponentFiltersForWordByRegexp, "l[ae]bassine+"); + } + case null, default -> fail("must be defined"); + } + } + case null, default -> { + fail("must be defined"); + } + } + return true; + })) ; + } else if (variableComponentFilters.size() != 1) { + fail("variableComponentFilters must not be empty"); + } + if (!CollectionUtils.isEmpty(variableComponentOrderBys) && variableComponentOrderBys.size() == 1) { + variableComponentOrderBys.stream().forEach(variableComponentOrderBy -> { + assertEquals("site_plateforme", variableComponentOrderBy.variableComponentKey().getId()); + assertEquals(DataRepository.Order.ASC, variableComponentOrderBy.order()); + }); + } else { + fail("variableComponentOrderBys must have size 1"); + } + } + + @Test + void TestSimpleSearch() throws JsonProcessingException { + fr.inra.oresing.model.data.DownloadDatasetQuery build = DownloadDatasetQuery.build(downloadDatasetQuerySimpleSearch); + assertTrue(build instanceof DownloadDatasetQuerySimpleSearch, "must be an advanced Search"); + DownloadDatasetQuerySimpleSearch advancedSearch = (DownloadDatasetQuerySimpleSearch) build; + Set<DownloadDatasetQuerySimpleSearch.AuthorizationDescription> expectedAuthorizationDescriptions = Set.of( + new DownloadDatasetQuerySimpleSearch.AuthorizationDescription( + new DownloadDatasetQuerySimpleSearch.IntervalValues("1984-01-01", "1984-01-02"), + List.of( + List.of( + new DownloadDatasetQuerySimpleSearch.RequiredAuthorization("projet", Ltree.fromSql("projet_manche")), + new DownloadDatasetQuerySimpleSearch.RequiredAuthorization("localization", Ltree.fromSql("plateforme")) + ) + ) + + ), + new DownloadDatasetQuerySimpleSearch.AuthorizationDescription( + new DownloadDatasetQuerySimpleSearch.IntervalValues("1984-01-03", "1984-01-04"), + List.of( + List.of( + new DownloadDatasetQuerySimpleSearch.RequiredAuthorization("projet", Ltree.fromSql("atlantique")), + new DownloadDatasetQuerySimpleSearch.RequiredAuthorization("localization", Ltree.fromSql("plateforme")) + ) + ) + + ) + ); + assertEquals(expectedAuthorizationDescriptions, advancedSearch.authorizationDescriptions()); + } + + @ParameterizedTest + @MethodSource("fr.inra.oresing.rest.model.data.DownloadDatasetQueryAdvancedSearchTest$TestErrorParams#params") + @Disabled //TODO + void testErrors(TestError testError) throws IOException { + String replacedJson = testError.from.replace( + testError.replace, + testError.by + ); + + DownloadDatasetQuery downloadDatasetQuery = (DownloadDatasetQuery) this.mapper.toObject( + replacedJson, + DownloadDatasetQuery.class); + downloadDatasetQuery = fixture.addApplication(downloadDatasetQuery); + try { + fr.inra.oresing.model.data.DownloadDatasetQuery build = DownloadDatasetQuery.build(downloadDatasetQuery); + fail(); + } catch (BadDownloadDatasetQuery e) { + assertEquals(testError.message, e.getMessage()); + comparemap(testError.params, e.getParams()); + testError.params.entrySet().stream() + .forEach(entry -> { + assertTrue(e.getParams().containsKey(entry.getKey()), "params must contains %s".formatted(entry.getKey())); + assertEquals(entry.getValue(), e.getParams().get(entry.getKey()), "params %s must be %s".formatted(entry.getKey(), entry.getValue())); + }); + } + } + + void comparemap(Map<String, Object> map1, Map map2) { + assertEquals(map1.size(), map2.size()); + map1.entrySet().stream() + .forEach(entry -> { + assertTrue(map2.containsKey(entry.getKey()), "params must contains %s".formatted(entry.getKey())); + assertEquals(entry.getValue(), map2.get(entry.getKey()), "params %s must be %s".formatted(entry.getKey(), entry.getValue())); + }); + } + + private void testFormat(DownloadDatasetQueryAdvancedSearch.WithFormat withFormat, String format) { + assertEquals(format, withFormat.format()); + } + + private void testIntervalValue(DownloadDatasetQueryAdvancedSearch.IntervalValues intervalValues, String from, String to) { + assertEquals(from, intervalValues.from(), "Interval value from must be %s".formatted(from)); + assertEquals(to, intervalValues.to(), "Interval value to must be %s".formatted(to)); + } + + record TestError( + String from, + String replace, + String by, + String message, + Map<String, Object> params + + ) { + } + + public class TestErrorParams { + ; + + static Stream<TestError> params() { + + String advancedSearchJson = """ + { + "application": null, + "applicationNameOrId": null, + "dataType": null, + "offset": null, + "limit": 15, + "variableComponentSelects": [{ + "variable": "Date", + "component": "day" + }, + { + "variable": "Date", + "component": "time" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Date", + "component": "day" + }, + "type": "date", + "format": "dd/MM/yyyy", + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + }, + { + "variableComponentKey": { + "variable": "Date", + "component": "time" + }, + "type": "time", + "format": "HH:mm:ss", + "intervalValues": { + "from": "12:20:45", + "to": "16:21:32" + } + }, + { + "variableComponentKey": { + "variable": "Date", + "component": "datetime" + }, + "type": "datetime", + "format": "dd/MM/yyyy HH:mm:ss", + "intervalValues": { + "from": "01/01/1984 12:20:45", + "to": "01/01/1984 16:21:32" + } + }, + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "type": "numeric", + "intervalValues": { + "from": "5", + "to": "40" + }, + "isRegExp": null + }, + { + "variableComponentKey": { + "variable": "date", + "component": "day" + }, + "type": "date", + "filter": "01/01/1984", + "format": "dd/MM/yyyy" + }, + { + "variableComponentKey": { + "variable": "date", + "component": "datetime" + }, + "type": "datetime", + "filter": "01/01/1984 01:12:43", + "format": "dd/MM/yyyy hh:mm:ss" + }, + { + "variableComponentKey": { + "variable": "date", + "component": "time" + }, + "type": "time", + "filter": "01:12:43", + "format": "hh:mm:ss" + }, + { + "variableComponentKey": { + "variable": "individus", + "component": "value" + }, + "type": "numeric", + "filter": "12.3" + }, + { + "variableComponentKey": { + "variable": "site", + "component": "bassin" + }, + "filter": "lebassin" + }, + { + "variableComponentKey": { + "variable": "site", + "component": "projet" + }, + "filter": "l[ae]bassine+", + "intervalValues": null, + "isRegExp": true + }, + { + "variableComponentKey": { + "variable": "projet", + "component": "value" + }, + "filter": "projet_manche", + "type": "reference" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + }"""; + return Stream.of( + new TestError( + advancedSearchJson, + """ + , + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + """, + "", + BadDownloadDatasetQuery.FILTER_MISSING_FILTER_OR_INTERVAL, + Map.of( + "variable", "Date", + "component", "day" + ) + ), + new TestError( + advancedSearchJson, + """ + "type": "date", + """, + "", + BadDownloadDatasetQuery.MISSING_TYPE_FOR_INTERVAL_VALUE, + Map.of( + "provided", "null", + "accepted", "date,time,datetime,numeric", + "variable", "Date", + "component", "day" + ) + ), + new TestError( + advancedSearchJson, + """ + "format": "dd/MM/yyyy", + """, + "", + BadDownloadDatasetQuery.MISSING_FORMAT_FOR_INTERVAL_VALUE, + + Map.of( + "variable", "Date", + "component", "day" + ) + ) + ); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryTest.java b/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d7ce4d69dcdcd783a7402f5db1a26c0d45a47d71 --- /dev/null +++ b/src/test/java/fr/inra/oresing/rest/model/data/DownloadDatasetQueryTest.java @@ -0,0 +1,284 @@ +package fr.inra.oresing.rest.model.data; + +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import fr.inra.oresing.persistence.JsonRowMapper; +import fr.inra.oresing.persistence.requestBuilder.datatype.DataTypeRequestBuilder; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.FileCopyUtils; +import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Tag("MODEL_REQUEST_TEST") +class DownloadDatasetQueryTest { + String simpleSearchJson = """ + { + "limit": 10, + "variableComponentSelects": [{ + "variable": "date", + "component": "value" + }, + { + "variable": "site", + "component": "plateforme" + } + ], + "authorizationDescriptions": [ + { + "timeScope": { + "from": "1984-01-01", + "to": "1984-01-02" + }, + "requiredAuthorizations": [ + { + "projet": "projet_manche", + "localization": "plateforme" + } + ] + }, + { + "timeScope": { + "from": "1984-01-03", + "to": "1984-01-04" + }, + "requiredAuthorizations": [ + { + "projet": "atlantique", + "localization": "plateforme" + } + ] + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + } + """; + + String searchByIds = """ + { + "limit": 10, + "variableComponentSelects": [{ + "variable": "date", + "component": "value" + }, + { + "variable": "site", + "component": "plateforme" + } + ], + "rowIds" : [ + "b5e086ee-0462-41b0-a136-d6e0604fe6c8", + "2386667c-6929-4981-9e8a-72c1d090272a" + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + } + """; + + String searchNoFilter = """ + { + "limit": 10, + "variableComponentSelects": [{ + "variable": "date", + "component": "value" + }, + { + "variable": "site", + "component": "plateforme" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + } + """; + + String advancedSearchJson = """ + { + "application": null, + "applicationNameOrId": null, + "dataType": null, + "offset": null, + "limit": 15, + "variableComponentSelects": [{ + "variable": "date", + "component": "value" + }, + { + "variable": "site", + "component": "plateforme" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "date", + "component": "value" + }, + "type": "date", + "format": "dd/MM/yyyy", + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + }, + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "type": "numeric", + "intervalValues": { + "from": "5", + "to": "40" + }, + "isRegExp": null + }, + { + "variableComponentKey": { + "variable": "site", + "component": "bassin" + }, + "filter": "lebassin" + }, + { + "variableComponentKey": { + "variable": "site", + "component": "chemin" + }, + "filter": "l[ae]bassine+", + "intervalValues": null, + "isRegExp": true + }, + { + "variableComponentKey": { + "variable": "projet", + "component": "value" + }, + "filter": "projet_manche", + "type": "reference" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "site", + "component": "plateforme" + }, + "order": "ASC", + "type": null, + "format": null + } + ] + }"""; + Resource yaml = new ClassPathResource("data/monsore/monsore-with-repository.yaml"); + + @Test + public void BuildSQLBySimpleSearchByRequest() throws IOException { + try { + fr.inra.oresing.model.data.DownloadDatasetQuery build = before(simpleSearchJson); + DataTypeRequestBuilder.SqlRequest sql = DataTypeRequestBuilder.buildSelectRequest(build); + System.out.println(sql); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Test + public void BuildSQLBySimpleSearchByRowIds() throws IOException { + try { + fr.inra.oresing.model.data.DownloadDatasetQuery build = before(searchByIds); + DataTypeRequestBuilder.SqlRequest request = DataTypeRequestBuilder.buildSelectRequest(build); + System.out.println(request.sql()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Test + public void BuildSQLNoFilter() throws IOException { + try { + fr.inra.oresing.model.data.DownloadDatasetQuery build = before(searchNoFilter); + DataTypeRequestBuilder.SqlRequest request = DataTypeRequestBuilder.buildSelectRequest(build); + System.out.println(request.sql()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Test + public void BuildSQLAdvancedSearch() throws IOException { + try { + fr.inra.oresing.model.data.DownloadDatasetQuery build = before(advancedSearchJson); + DataTypeRequestBuilder.SqlRequest request = DataTypeRequestBuilder.buildSelectRequest(build); + System.out.println(request.sql()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @NotNull + private fr.inra.oresing.model.data.DownloadDatasetQuery before(String search) throws IOException { + byte[] yamlContent = FileCopyUtils.copyToByteArray(yaml.getInputStream()); + YAMLMapper mapper = new YAMLMapper(); + Configuration configuration = mapper.readValue(yamlContent, Configuration.class); + ImmutableSet.Builder<String> requiredAuthorizationsAttributesBuilder = ImmutableSet.builder(); + + for (Map.Entry<String, Configuration.DataTypeDescription> dataTypeEntry : configuration.getDataTypes().entrySet()) { + Configuration.AuthorizationDescription authorization = dataTypeEntry.getValue().getAuthorization(); + if (authorization != null) { + LinkedHashMap<String, Configuration.AuthorizationScopeDescription> authorizationScopesVariableComponentKey = authorization.getAuthorizationScopes(); + requiredAuthorizationsAttributesBuilder.addAll(authorizationScopesVariableComponentKey.keySet()); + } + } + configuration.setRequiredAuthorizationsAttributes(List.copyOf(requiredAuthorizationsAttributesBuilder.build())); + DownloadDatasetQuery downloadDatasetQuerySearch = (DownloadDatasetQuery) new JsonRowMapper().toObject(search, DownloadDatasetQuery.class); + Application application = new Application(); + application.setConfiguration(configuration); + application.setName("monsores"); + downloadDatasetQuerySearch.setApplication(application); + downloadDatasetQuerySearch.setDataType("pem"); + fr.inra.oresing.model.data.DownloadDatasetQuery build = DownloadDatasetQuery.build(downloadDatasetQuerySearch); + return build; + } + +} \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/model/data/Fixture.java b/src/test/java/fr/inra/oresing/rest/model/data/Fixture.java new file mode 100644 index 0000000000000000000000000000000000000000..e31b3aacb3e5852bb1bd3b975af7e3b250738718 --- /dev/null +++ b/src/test/java/fr/inra/oresing/rest/model/data/Fixture.java @@ -0,0 +1,43 @@ +package fr.inra.oresing.rest.model.data; + +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import fr.inra.oresing.model.Application; +import fr.inra.oresing.model.Configuration; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Component; +import org.springframework.util.FileCopyUtils; +import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class Fixture { + public static final String DATATYPE = "pem"; + public static Resource yaml = new ClassPathResource("data/monsore/monsore-with-repository.yaml"); + public static DownloadDatasetQuery addApplication(DownloadDatasetQuery downloadDatasetQuery) throws IOException { + byte[] yamlContent = FileCopyUtils.copyToByteArray(yaml.getInputStream()); + YAMLMapper mapper = new YAMLMapper(); + Configuration configuration = mapper.readValue(yamlContent, Configuration.class); + ImmutableSet.Builder<String> requiredAuthorizationsAttributesBuilder = ImmutableSet.builder(); + + for (Map.Entry<String, Configuration.DataTypeDescription> dataTypeEntry : configuration.getDataTypes().entrySet()) { + Configuration.AuthorizationDescription authorization = dataTypeEntry.getValue().getAuthorization(); + if (authorization != null) { + LinkedHashMap<String, Configuration.AuthorizationScopeDescription> authorizationScopesVariableComponentKey = authorization.getAuthorizationScopes(); + requiredAuthorizationsAttributesBuilder.addAll(authorizationScopesVariableComponentKey.keySet()); + } + } + configuration.setRequiredAuthorizationsAttributes(List.copyOf(requiredAuthorizationsAttributesBuilder.build())); + configuration.setRequiredAuthorizationsAttributes(List.copyOf(requiredAuthorizationsAttributesBuilder.build())); + Application application = new Application(); + application.setConfiguration(configuration); + downloadDatasetQuery.setApplication(application); + downloadDatasetQuery.setDataType(DATATYPE); + return downloadDatasetQuery; + + } +} diff --git a/src/test/resources/application-tests.properties b/src/test/resources/application-tests.properties index 28bacc0f784cd4e30f5be357f238d6e98a69a1ca..047f7c54bfbb690667f914df3896dc773fd8e441 100644 --- a/src/test/resources/application-tests.properties +++ b/src/test/resources/application-tests.properties @@ -2,7 +2,7 @@ # org.postgresql.util.PSQLException: ERROR: cached plan must not change result type # qui est dû au fait que entre deux tests, flyway écrase et recrée la base de données # or, si on réutilise un preparedStatement créé sur le schéma du test précédent, ça échoue -spring.datasource.url=jdbc:tc:postgresql:13.11:///test?preparedStatementCacheQueries=0 +spring.datasource.url=jdbc:tc:postgresql:16.0:///test?preparedStatementCacheQueries=0 spring.datasource.username=test spring.datasource.password=test diff --git a/src/test/resources/data/multiplicity/data/bugs.csv b/src/test/resources/data/multiplicity/data/bugs.csv index fe6999c326a955fba5f22490b93416af15dec746..a68969e705bb6332af0f1cb137f9b4f962fdaab3 100644 --- a/src/test/resources/data/multiplicity/data/bugs.csv +++ b/src/test/resources/data/multiplicity/data/bugs.csv @@ -1,2 +1,2 @@ -no;fichiers;dates;projets;durations;reference1 +nos;fichiers;dates;projets;durations;references 1;file1,file2;23/01/2002,24/01/2002;1,2;3.2,5.4;toto__toto1,tutu__tutu1 \ No newline at end of file diff --git a/src/test/resources/data/multiplicity/multiplicity.yaml b/src/test/resources/data/multiplicity/multiplicity.yaml index 418c2fd0651b0d684310ba7ac6c93e0aee8ccf39..ea789eac75d4746eb249dff7cb348726addddc23 100644 --- a/src/test/resources/data/multiplicity/multiplicity.yaml +++ b/src/test/resources/data/multiplicity/multiplicity.yaml @@ -62,7 +62,7 @@ dataTypes: data: bug: components: - no: + nos: checker: name: Integer params: @@ -94,21 +94,72 @@ dataTypes: params: required: false multiplicity: "MANY" - reference1: + references: checker: name: Reference params: multiplicity: MANY refType: reference1 + computedComponents: + no: + computation: + expression: datum.bug.nos.value.split(",")[0] + checker: + name: Integer + params: + required: true + multiplicity: "ONE" + fichier: + computation: + expression: datum.bug.fichiers.value.split(",")[0] + checker: + name: String + params: + pattern: .* + required: true + multiplicity: "ONE" + date: + computation: + expression: datum.bug.dates.value.split(",")[0] + checker: + name: Date + params: + pattern: dd/MM/yyyy + required: false + multiplicity: "ONE" + projet: + computation: + expression: datum.bug.projets.value.split(",")[0] + checker: + name: Integer + params: + required: true + multiplicity: "ONE" + duration: + computation: + expression: datum.bug.durations.value.split(",")[0] + checker: + name: Float + params: + required: false + multiplicity: "ONE" + reference: + computation: + expression: datum.bug.references.value.split(",")[0] + checker: + name: Reference + params: + multiplicity: ONE + refType: reference1 format: headerLine: 1 firstRowLine: 2 columns: - - header: no + - header: nos boundTo: variable: bug - component: no + component: nos - header: fichiers boundTo: variable: bug @@ -125,10 +176,10 @@ dataTypes: boundTo: variable: bug component: durations - - header: reference1 + - header: references boundTo: variable: bug - component: reference1 + component: references uniqueness: - variable: bug component: no \ No newline at end of file diff --git a/src/test/resources/http/application/LoadACBB.http b/src/test/resources/http/application/LoadACBB.http new file mode 100644 index 0000000000000000000000000000000000000000..7721a2503b91d786b6789614dcae7d0307d350c2 --- /dev/null +++ b/src/test/resources/http/application/LoadACBB.http @@ -0,0 +1,127 @@ +### login + +POST localhost:8081/api/v1/login?login=poussin&password=xxxx +Content-Type: application/json + +{} + +### Load Application +POST localhost:8081/api/v1/applications/acbb +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="element-name" +Content-Type: text/plain + +L'application ACBB +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="monsores.yaml" +Content-Type: application/json + +< ../../data/acbb/acbb.yaml +--WebAppBoundary-- + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### add agroecosystemes +POST localhost:8081/api/v1/applications/acbb/references/agroecosystemes +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="agroecosystemes.csv" +Content-Type: application/json + +< ../../data/acbb/agroecosysteme.csv +--WebAppBoundary-- + +### add sites +POST localhost:8081/api/v1/applications/acbb/references/sites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="sites.csv" +Content-Type: application/json + +< ../../data/acbb/sites.csv +--WebAppBoundary-- + +### add parcelle +POST localhost:8081/api/v1/applications/acbb/references/parcelles +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="parcelle.csv" +Content-Type: application/json + +< ../../data/acbb/parcelle.csv +--WebAppBoundary-- + +### add unites +POST localhost:8081/api/v1/applications/acbb/references/unites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="unites.csv" +Content-Type: application/json + +< ../../data/acbb/unites.csv +--WebAppBoundary-- + +### add modalites +POST localhost:8081/api/v1/applications/acbb/references/modalites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="modalites.csv" +Content-Type: application/json + +< ../../data/acbb/modalites.csv +--WebAppBoundary-- + +### add version_de_traitement +POST localhost:8081/api/v1/applications/acbb/references/version_de_traitement +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="version_de_traitement.csv" +Content-Type: application/json + +< ../../data/acbb/version_de_traitement.csv +--WebAppBoundary-- + +### add biomasse_production_teneur +POST localhost:8081/api/v1/applications/acbb/data/biomasse_production_teneur +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="biomasse_production_teneur.csv" +Content-Type: application/json + +< ../../data/acbb/biomasse_production_teneur.csv +--WebAppBoundary-- + +### add flux_tours +POST localhost:8081/api/v1/applications/acbb/data/flux_tours +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="biomasse_production_teneur.csv" +Content-Type: application/json + +< ../../data/acbb/Flux_tours.csv +--WebAppBoundary-- + +### add SWC +POST localhost:8081/api/v1/applications/acbb/data/SWC +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="SWC.csv" +Content-Type: application/json + +< ../../data/acbb/SWC.csv +--WebAppBoundary-- \ No newline at end of file diff --git a/src/test/resources/http/application/LoadMonSores.http b/src/test/resources/http/application/LoadMonSores.http new file mode 100644 index 0000000000000000000000000000000000000000..65813b17969f478512f96d47bc51ef84d987f95b --- /dev/null +++ b/src/test/resources/http/application/LoadMonSores.http @@ -0,0 +1,378 @@ +### login + +POST localhost:8081/api/v1/login?login=poussin&password=xxxx +Content-Type: application/json + +{} + +### Load Application +POST localhost:8081/api/v1/applications/monsores +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="element-name" +Content-Type: text/plain + +L'application monsoere +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="monsores.yaml" +Content-Type: application/json + +< ../../data/monsore/monsore-with-repository.yaml +--WebAppBoundary-- + + + referentielFiles.put("variables", "/data/monsore/refdatas/variables.csv"); + referentielFiles.put("variables_et_unites_par_types_de_donnees", "/data/monsore/refdatas/variables_et_unites_par_types_de_donnees.csv"); + +### add type_de_sites +POST localhost:8081/api/v1/applications/monsores/references/type_de_sites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="type_de_sites.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/type_de_sites.csv +--WebAppBoundary-- + +### add sites +POST localhost:8081/api/v1/applications/monsores/references/sites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="sites.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/sites.csv +--WebAppBoundary-- + +### add especes +POST localhost:8081/api/v1/applications/monsores/references/especes +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="especes.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/especes.csv +--WebAppBoundary-- + +### add projet +POST localhost:8081/api/v1/applications/monsores/references/projet +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="projet.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/projet.csv +--WebAppBoundary-- + +### add themes +POST localhost:8081/api/v1/applications/monsores/references/themes +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="themes.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/themes.csv +--WebAppBoundary-- + +### add site_theme_datatype +POST localhost:8081/api/v1/applications/monsores/references/site_theme_datatype +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="types_de_donnees_par_themes_de_sites_et_projet.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/types_de_donnees_par_themes_de_sites_et_projet.csv +--WebAppBoundary-- + +### add unites +POST localhost:8081/api/v1/applications/monsores/references/unites +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="unites.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/unites.csv +--WebAppBoundary-- + +### add reference1 +POST localhost:8081/api/v1/applications/monsores/references/valeurs_qualitatives +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="valeurs_qualitatives.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/valeurs_qualitatives.csv +--WebAppBoundary-- + +### add variables +POST localhost:8081/api/v1/applications/monsores/references/variables +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="variables.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/variables.csv +--WebAppBoundary-- + +### add variables_et_unites_par_types_de_donnees +POST localhost:8081/api/v1/applications/monsores/references/variables_et_unites_par_types_de_donnees +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="variables_et_unites_par_types_de_donnees.csv" +Content-Type: application/json + +< ../../data/monsore/refdatas/variables_et_unites_par_types_de_donnees.csv +--WebAppBoundary-- + +### add data atlantique-nivelle-p1-pem +POST localhost:8081/api/v1/applications/monsores/data/pem +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="atlantique-nivelle-p1-pem.csv" +Content-Type: text/csv + +< ../../data/monsore/atlantique-nivelle-p1-pem.csv + +--WebAppBoundary-- +Content-Disposition: form-data; name="params"; + +{ + "fileid":"fa19d8d9-e802-4176-bcae-b33cde3960e7", + "binaryfiledataset":{ + "datatype":"pem", + "requiredAuthorizations":{ + "projet":"projet_atlantique", + "localization":"plateforme.nivelle.nivelle__p1" + }, + "from":"1983-12-31 23:00:00", + "to":"1984-01-05 23:00:00", + "comment":null + }, + "topublish":true +} +--WebAppBoundary-- + +### add data atlantique-nivelle-p1-pem +POST localhost:8081/api/v1/applications/monsores/data/pem +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="manche-oir-p1-pem.csv" +Content-Type: text/csv + +< ../../data/monsore/manche-oir-p1-pem.csv + +--WebAppBoundary-- +Content-Disposition: form-data; name="params"; + +{ + "fileid":"fa19d8d9-e802-4176-bcae-b33cde3960e7", + "binaryfiledataset":{ + "datatype":"pem", + "requiredAuthorizations":{ + "projet":"projet_manche", + "localization":"plateforme.oir.oir__p1" + }, + "from":"1983-12-31 23:00:00", + "to":"1984-01-05 23:00:00", + "comment":null + }, + "topublish":true +} +--WebAppBoundary-- + +### get data all +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json + +### get data by authorization +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "authorizationDescriptions": [ + { + "timeScope": { + "from": "1984-01-01", + "to": "1984-01-03" + }, + "requiredAuthorizations": [ + { + "projet": "projet_manche", + "localization": "plateforme" + } + ] + } + ] + } + + +### get data by site bassin : contains +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "site", + "component": "bassin" + }, + "filter": "nivelle" + } + ] + } + +### get data by site bassin and espèce value : regex +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "espece", + "component": "value" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "espece", + "component": "value" + }, + "filter": "(al|ang)", + "isRegExp": "true" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "espece", + "component": "value" + }, + "order": "ASC" + } + ] + } + +### get data by site bassin and espèce value : reference +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "site", + "component": "chemin" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "site", + "component": "chemin" + }, + "filter": "plateforme.nivelle" + } + ] + } + +### get data by site bassin and espèce value : number +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "Nombre d'individus", + "component": "value" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "Nombre d'individus", + "component": "value" + }, + "filter": 41 + } + ] + } + +### get data by site bassin and espèce value : date +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "date", + "component": "value" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "date", + "component": "value" + }, + "filter": "01/01/1984" + } + ] + } + +### get data by site bassin and espèce value : date +GET http://localhost:8081/api/v1/applications/monsores/data/pem/json? + downloadDatasetQuery= + { + "offset":0, + "limit":68, + "variableComponentSelects":[ + { + "variable":"date", + "component":"value" + }, + { + "variable":"site", + "component":"bassin" + }, + { + "variable":"site", + "component":"chemin" + }, + { + "variable":"site", + "component":"plateforme" + }, + { + "variable":"Couleur des individus", + "component":"unit" + }, + { + "variable":"Couleur des individus", + "component":"value" + }, + { + "variable":"Nombre d'individus", + "component":"unit" + }, + { + "variable":"Nombre d'individus", + "component":"value" + }, + { + "variable":"espece", + "component":"value" + } + ], + "variableComponentFilters":[ ], + "variableComponentOrderBy":[ ], + "authorizationDescriptions":[ ], + "dataType":"pem", + "applicationNameOrId":"monsores" + } \ No newline at end of file diff --git a/src/test/resources/http/application/LoadMultiplicity.http b/src/test/resources/http/application/LoadMultiplicity.http new file mode 100644 index 0000000000000000000000000000000000000000..aa5b5eef607fa06dcd3096fd6292ca77df5df154 --- /dev/null +++ b/src/test/resources/http/application/LoadMultiplicity.http @@ -0,0 +1,912 @@ +### login + +POST localhost:8081/api/v1/login?login=poussin&password=xxxx +Content-Type: application/json + +{} + +### Load Application +POST localhost:8081/api/v1/applications/multiplicity +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="element-name" +Content-Type: text/plain + +L'application couteau suisse +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="multiplicity.yaml" +Content-Type: application/json + +< ../../data/multiplicity/multiplicity.yaml +--WebAppBoundary-- + +### add reference1 +POST localhost:8081/api/v1/applications/multiplicity/references/reference1 +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="reference1.csv" +Content-Type: application/json + +< ../../data/multiplicity/references/reference1.csv +--WebAppBoundary-- + +### add reference2 +POST localhost:8081/api/v1/applications/multiplicity/references/reference2 +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="reference2.csv" +Content-Type: application/json + +< ../../data/multiplicity/references/reference2.csv +--WebAppBoundary-- + +### add data +POST localhost:8081/api/v1/applications/multiplicity/data/bugs +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="bugs.csv" +Content-Type: application/json + +< ../../data/multiplicity/data/bugs.csv +--WebAppBoundary-- + +### get data all +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json + +### get data with select +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "date" + }, + { + "variable": "bug", + "component": "references" + } + ] + } + +### get data by id +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "rowIds" : [ + "59ae5fec-8ed5-495b-b201-5475ea50906e" + ] + } + +### get data by criteria +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "type": "date", + "format": "dd/MM/yyyy", + "intervalValues": { + "from": "01/01/1984", + "to": "04/01/1984" + } + } + ] + } + + +### get data by bug and nos value : number[] +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "nos" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "filter": 1 + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and reference value : reference +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "reference" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "reference" + }, + "filter": "toto__toto1" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and references value : reference[] +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "references" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "references" + }, + "filter": "toto__toto1" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + + +### get data by bug and fichier : contains +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "fichier" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "fichier" + }, + "filter": "ile" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and fichiers : contains[] +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "fichiers" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "fichiers" + }, + "filter": "ile" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + + +### get data by bug and date +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "date" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "filter": "23/01/2002" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and date as array +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "dates" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "filter": "24/01/2002" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and date by interval +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "date" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "intervalValues": { + "from":"23/01/2002", + "to":"24/01/2002" + } + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and date by interval for array +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "dates" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "intervalValues": { + "from":"23/01/2002", + "to":"24/01/2002" + } + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and numeric by interval +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "no" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + }, + "intervalValues": { + "from":"0", + "to":"4" + } + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + +### get data by bug and numeric by interval for array +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "nos" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "intervalValues": { + "from":"0", + "to":"4" + } + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); +%} + + +### get data with all select, filter, order by +GET http://localhost:8081/api/v1/applications/multiplicity/data/bugs/json? + downloadDatasetQuery= + { + "variableComponentSelects": [ + { + "variable": "bug", + "component": "no" + }, + { + "variable": "bug", + "component": "nos" + }, + { + "variable": "bug", + "component": "date" + }, + { + "variable": "bug", + "component": "dates" + }, + { + "variable": "bug", + "component": "projet" + }, + { + "variable": "bug", + "component": "projets" + }, + { + "variable": "bug", + "component": "fichier" + }, + { + "variable": "bug", + "component": "fichiers" + }, + { + "variable": "bug", + "component": "duration" + }, + { + "variable": "bug", + "component": "durations" + }, + { + "variable": "bug", + "component": "reference" + }, + { + "variable": "bug", + "component": "references" + } + ], + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + }, + "intervalValues": { + "from": "0", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "intervalValues": { + "from": "0", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "filter": "23/01/2002" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "intervalValues": { + "from": "23/01/2002", + "to": "24/01/2002" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "filter": "23/01/2002" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "intervalValues": { + "from": "23/01/2002", + "to": "24/01/2002" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projet" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projet" + }, + "intervalValues": { + "from": "1", + "to": "2" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projets" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projets" + }, + "intervalValues": { + "from": "1", + "to": "2" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "duration" + }, + "filter": "3.20" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "duration" + }, + "intervalValues": { + "from": "3", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "durations" + }, + "filter": "3.20" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "durations" + }, + "intervalValues": { + "from": "3", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "reference" + }, + "filter": "toto__toto1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "references" + }, + "filter": "toto__toto1" + } + ], + "variableComponentOrderBy": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projet" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projets" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "fichier" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "fichiers" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "duration" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "durations" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "reference" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "references"}} + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("test no", function() { + client.assert(response.body.rows[0].values.bug.no == 1, "no = 1"); + }); + client.test("test date", function() { + client.assert(response.body.rows[0].values.bug.date == 'date:2002-01-23T00:00:00:dd/MM/yyyy', "date = 'date:2002-01-23T00:00:00:dd/MM/yyyy'"); + }); + client.test("test duration", function() { + client.assert(response.body.rows[0].values.bug.duration == 3.2, "duration = 3.2"); + }); + client.test("test reference", function() { + client.assert(response.body.rows[0].values.bug.reference == 'toto__toto1', "reference = 'toto__toto1'"); + }); + client.test("test fichier", function() { + client.assert(response.body.rows[0].values.bug.fichier == 'file1', "fichier = 'file1"); + }); + client.test("test nos", function() { + client.assert(response.body.rows[0].values.bug.nos == 1, "bad nos"); + }); + client.test("test dates", function() { + client.assert(response.body.rows[0].values.bug.dates.every((val, index) => val === [ + 'date:2002-01-23T00:00:00:dd/MM/yyyy', + 'date:2002-01-24T00:00:00:dd/MM/yyyy' + ][index]) , "bad dates"); + }); + client.test("test durations", function() { + client.assert(response.body.rows[0].values.bug.durations.every((val, index) => val === [ + 3.2, + 5.4 + ][index]) , "bad duration"); + }); + client.test("test references", function() { + client.assert(response.body.rows[0].values.bug.references.every((val, index) => val === [ + "toto__toto1", + "tutu__tutu1" + ][index]) , "bad references"); + }); + client.test("test fichiers", function() { + client.assert(response.body.rows[0].values.bug.fichiers.every((val, index) => val === [ + "file1", + "file2" + ][index]) , "bad fichiers"); + }); +%} + +### get data with all select, filter, order by +DELETE http://localhost:8081/api/v1/applications/multiplicity/data/bugs? + downloadDatasetQuery= + { + "variableComponentFilters": [ + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "no" + }, + "intervalValues": { + "from": "0", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "nos" + }, + "intervalValues": { + "from": "0", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "filter": "23/01/2002" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "date" + }, + "intervalValues": { + "from": "23/01/2002", + "to": "24/01/2002" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "filter": "23/01/2002" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "dates" + }, + "intervalValues": { + "from": "23/01/2002", + "to": "24/01/2002" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projet" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projet" + }, + "intervalValues": { + "from": "1", + "to": "2" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projets" + }, + "filter": "1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "projets" + }, + "intervalValues": { + "from": "1", + "to": "2" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "duration" + }, + "filter": "3.20" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "duration" + }, + "intervalValues": { + "from": "3", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "durations" + }, + "filter": "3.20" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "durations" + }, + "intervalValues": { + "from": "3", + "to": "4" + } + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "reference" + }, + "filter": "toto__toto1" + }, + { + "variableComponentKey": { + "variable": "bug", + "component": "references" + }, + "filter": "toto__toto1" + } + ] + } + +> {% + client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); + }); + client.test("test no", function() { + client.assert(response.body.length == 36, "response is not an uuid"); + }); +%} + diff --git a/src/test/resources/http/monsore-with-repository.yaml b/src/test/resources/http/monsore-with-repository.yaml new file mode 100644 index 0000000000000000000000000000000000000000..03f49f3209040f3a68009ad18097a465c3b645c3 --- /dev/null +++ b/src/test/resources/http/monsore-with-repository.yaml @@ -0,0 +1,774 @@ +version: 1 +application: + defaultLanguage: fr + internationalizationName: + fr: SOERE mon SOERE avec dépôt + en: SOERE my SOERE with repository + name: MONSORES + version: 1 + +tags: + context: + fr: contexte + en: context + data: + fr: données + en: data + test: + fr: test + en: test + unit: + fr: unité + en: unit +rightsRequest: + description: + fr: Vous pouvez demander des droits à l'application monsore en remplissant ce formulaire + en: You can request rights to the monsore application by filling out this form + format: + organization: + internationalizationName: + fr: Nom de l'organisme de recherche + en: Name of research organization + checker: + name: String + params: + pattern: ".*" + required: true + project: + internationalizationName: + fr: Description du projet de recherche + en: Description of the research project + checker: + name: String + params: + pattern: ".*" + required: false + startDate: + internationalizationName: + fr: Date de début du projet + en: Project start date + checker: + name: Date + params: + pattern: "dd/MM/yyyy" + endDate: + internationalizationName: + fr: Date de fin du projet + en: Project end date + checker: + name: Date + params: + pattern: "dd/MM/yyyy" + projectManagers: + internationalizationName: + fr: Responsables du projet + en: Project managers + checker: + name: String + params: + pattern: ".*" + required: false + multiplicity: MANY + +additionalFiles: + fichiers: + internationalizationName: + fr: Fichiers + en: Files + format: + nom: + internationalizationName: + fr: Nom + en: Name + checker: + name: String + params: + pattern: "[a-z]*" + date: + internationalizationName: + fr: Date + en: Date + checker: + name: Date + params: + pattern: "dd/MM/yyyy" + age: + internationalizationName: + fr: Age + en: Age + checker: + name: Integer + poids: + internationalizationName: + fr: Poids + en: Weight + checker: + name: Float + params: + required: false + site: + internationalizationName: + fr: Site + en: Place + checker: + name: Reference + params: + refType: sites + required: true + utilisateurs: + internationalizationName: + fr: Users + en: User + format: + nom: + internationalizationName: + fr: Nom + en: Name + checker: + name: String + params: + pattern: "[a-z]*" + prenom: + internationalizationName: + fr: Prénom + en: Surname + checker: + name: String + params: + pattern: "[a-z]*" + +compositeReferences: + sites: + components: + - reference: type_de_sites + - parentKeyColumn: tze_type_nom + parentRecursiveKey: zet_chemin_parent + reference: sites + projet: + components: + - reference: projet +references: + especes: + tags: [data] + internationalizationName: + fr: Espèces + en: Species + internationalizedColumns: + esp_definition_fr: + fr: esp_definition_fr + en: esp_definition_en + internationalizationDisplay: + pattern: + fr: '{esp_nom}' + en: '{esp_nom}' + keyColumns: + - esp_nom + columns: + esp_nom: + tags: [test] + esp_definition_fr: null + esp_definition_en: null + colonne_homonyme_entre_referentiels: null + computedColumns: + my_computed_column: + tags: [__hidden__] + computation: + expression: > + return "my value"; + projet: + tags: [context, data, test] + internationalizationName: + fr: Projet + en: Project + internationalizedColumns: + nom_key: + fr: nom_fr + en: nom_en + definition_fr: + fr: definition_fr + en: definition_en + internationalizationDisplay: + pattern: + fr: '{nom_key}' + en: '{nom_key}' + keyColumns: + - nom_key + columns: + nom_key: null + nom_fr: null + nom_en: null + definition_fr: null + definition_en: null + colonne_homonyme_entre_referentiels: null + sites: + tags: [context] + validations: + typeSitesRef: + internationalizationName: + fr: référence au type de site + checker: + name: Reference + params: + refType: type_de_sites + columns: [ tze_type_nom ] + siteParentRef: + internationalizationName: + fr: référence à la colonne parent + checker: + name: Reference + params: + refType: sites + required: false + columns: [ zet_chemin_parent ] + keyColumns: + - zet_chemin_parent + - zet_nom_key + internationalizationName: + fr: Site + en: Site + internationalizedColumns: + zet_nom_key: + fr: zet_nom_fr + en: zet_nom_en + zet_description_fr: + fr: zet_description_fr + en: zet_description_en + internationalizationDisplay: + pattern: + fr: '{zet_nom_key}' + en: '{zet_nom_key}' + columns: + tze_type_nom: null + zet_nom_key: null + zet_nom_fr: null + zet_nom_en: null + zet_description_fr: null + zet_description_en: null + zet_chemin_parent: null + themes: + tags: [context] + keyColumns: + - nom_key + internationalizationName: + fr: Thème + en: Thematic + internationalizedColumns: + nom_key: + fr: nom_fr + en: nom_en + description_fr: + fr: description_fr + en: description_en + internationalizationDisplay: + pattern: + fr: '{nom_key}' + en: '{nom_key}' + columns: + nom_key: null + nom_fr: null + nom_en: null + description_fr: null + description_en: null + type_de_fichiers: + tags: ["__hidden__"] + keyColumns: + - nom_key + internationalizationName: + fr: Types de fichiers + en: Files types + internationalizedColumns: + nom_key: + fr: nom_fr + en: nom_en + description_fr: + fr: description_fr + en: description_en + internationalizationDisplay: + pattern: + fr: '{nom_key}' + en: '{nom_key}' + columns: + nom_key: null + nom_fr: null + nom_en: null + description_fr: null + description_en: null + type_de_sites: + tags: [context] + keyColumns: + - tze_nom_key + internationalizationName: + fr: Types de sites + en: Sites types + internationalizedColumns: + tze_nom_key: + fr: tze_nom_fr + en: tze_nom_en + tze_definition_fr: + fr: tze_definition_fr + en: tze_definition_en + internationalizationDisplay: + pattern: + fr: '{tze_nom_key}' + en: '{tze_nom_key}' + columns: + tze_nom_key: null + tze_nom_fr: null + tze_nom_en: null + tze_definition_fr: null + tze_definition_en: null + site_theme_datatype: + tags: [context] + internationalizationName: + fr: Types de données par site et projet + en: Data types by site and project + internationalizationDisplay: + pattern: + fr: >- + nom du projet: {projet}, nom du site : {site}, nom du + thème : {theme}, nom du type de données : {datatype} + en: >- + projet name: {projet}, site name : {site}, theme name : + {theme}, data type name : {datatype} + validations: + projetRef: + internationalizationName: + fr: référence au projet + checker: + name: Reference + params: + refType: projet + columns: [ projet ] + sitesRef: + internationalizationName: + fr: référence au site + checker: + name: Reference + params: + refType: sites + columns: [ site ] + themesRef: + internationalizationName: + fr: référence au theme + checker: + name: Reference + params: + refType: themes + columns: [ theme ] + checkDatatype: + internationalizationName: + fr: test + columns: [ datatype ] + checker: + name: GroovyExpression + params: + groovy: + expression: > + String datatype = Arrays.stream((String)datum.datatype).split("_") + .collect{it.substring(0, 1)} + .join(); + return application.getDataType().contains(datatype); + keyColumns: + - projet + - site + - theme + - datatype + columns: + projet: + headerName: nom du projet + site: + headerName: nom du site + theme: + headerName: nom du thème + datatype: + headerName: nom du type de données + unites: + tags: [data] + keyColumns: + - nom_key + internationalizationName: + fr: Unités + en: Units + internationalizedColumns: + code_key: + fr: code_fr + en: code_en + nom_key: + fr: nom_fr + en: nom_en + internationalizationDisplay: + pattern: + fr: '{nom_key} ({code_key})' + en: '{nom_key} ({code_key})' + columns: + code_key: null + code_fr: null + code_en: null + nom_key: null + nom_fr: null + nom_en: null + valeurs_qualitatives: + tags: [data] + keyColumns: + - nom_key + - valeur_key + internationalizationName: + fr: Valeurs qualitatives + en: Qualitative values + internationalizedColumns: + nom_key: + fr: nom_fr + en: nom_en + valeur_key: + fr: valeur_fr + en: valeur_en + internationalizationDisplay: + pattern: + fr: '{valeur_key}' + en: '{valeur_key}' + columns: + nom_key: null + nom_fr: null + nom_en: null + valeur_key: null + valeur_fr: null + valeur_en: null + variables: + tags: [data] + keyColumns: + - nom_key + internationalizationName: + fr: Variables + en: Variables + internationalizedColumns: + nom_key: + fr: nom_fr + en: nom_en + definition_fr: + fr: definition_fr + en: definition_en + internationalizationDisplay: + pattern: + fr: '{nom_key}' + en: '{nom_key}' + columns: + nom_key: null + nom_fr: null + nom_en: null + definition_fr: null + definition_en: null + is_qualitative: + headerName: isQualitative + variables_et_unites_par_types_de_donnees: + tags: [data] + validations: + variableRef: + internationalizationName: + fr: référence à la variable + checker: + name: Reference + params: + refType: variables + columns: [ variable ] + uniteRef: + internationalizationName: + fr: référence à l'unité' + checker: + name: Reference + params: + refType: unites + columns: [ unite ] + checkDatatype: + internationalizationName: + fr: test + columns: [ datatype ] + checker: + name: GroovyExpression + params: + groovy: + expression: > + String datatype = Arrays.stream((String)(datum.datatype).split("_")) + .collect{it.substring(0, 1)} + .join(); + return application.getDataType().contains(datatype); + keyColumns: + - datatype + - variable + internationalizationName: + fr: Variables et unités par type de données + en: Variables and units by data type + internationalizationDisplay: + pattern: + fr: >- + nom du type de données : {datatype}, nom de la variable + : {variable}, : nom de l'unité {unite} + en: >- + datatype name : {datatype}, variable name : {variable}, : unit name {unite} + columns: + datatype: + headerName: nom du type de données + variable: + headerName: nom de la variable + unite: + headerName: nom de l'unité +dataTypes: + pem: + tags: [context, data, test] + internationalizationName: + fr: Piégeage en Montée + en: Trap in ascent + internationalizationDisplays: + especes: + pattern: + fr: 'espèce :{esp_nom}' + en: 'espèce :{esp_nom}' + repository: + filePattern: "(.*)_(.*)_(.*)_(.*).csv" + authorizationScope: + localization: 1 + projet: 2 + startDate: + token: 3 + endDate: + token: 4 + data: + projet: + tags: [test] + components: + value: + tags: [test] + checker: + name: Reference + params: + refType: projet + site: + components: + bassin: null + plateforme: null + chemin: + defaultValue: + expression: > + return references.sites + .findAll(){it.refValues.zet_chemin_parent.equals((String)datum.site.bassin)} + .find{it.refValues.zet_nom_key.equals((String)datum.site.plateforme)} + .getHierarchicalKey();; + references: + - sites + checker: + name: Reference + params: + refType: sites + computedComponents: + site_bassin: + tags: [__hidden__] + computation: + expression: > + return references.sites + .find{it.getNaturalKey().equals((String)datum.site.bassin)} + .getHierarchicalKey(); + references: + - sites + checker: + name: Reference + params: + refType: sites + date: + components: + value: + checker: + name: Date + params: + pattern: dd/MM/yyyy + required: null + espece: + components: + value: + checker: + name: Reference + params: + refType: especes + Couleur des individus: + components: + value: + checker: + name: Reference + params: + refType: valeurs_qualitatives + unit: + defaultValue: + expression: return "sans_unite" + checker: + name: Reference + params: + refType: unites + required: null + Nombre d'individus: + components: + value: + defaultValue: + expression: return 0 + checker: + name: Integer + params: + required: null + unit: + defaultValue: + expression: return "sans_unite" + checker: + name: Reference + params: + refType: unites + required: null + validations: + unitOfColor: + internationalizationName: + fr: vérifie l'unité de la couleur des individus + checker: + name: GroovyExpression + params: + groovy: + expression: > + String datatype = "piegeage_en_montee"; + String variable = "Couleur des individus"; + String codeVariable = "couleur_des_individus"; + String component = "unit"; + return + referencesValues.site_theme_datatype + .findAll{it.datatype.equals(datatype)} + .find{it.variable.equals(codeVariable)} + .unite.equals((String)datum.variable.component) + references: + - variables_et_unites_par_types_de_donnees + unitOfIndividus: + internationalizationName: + fr: vérifie l'unité du nombre d'individus + checker: + name: GroovyExpression + params: + groovy: + expression: > + String datatype = "piegeage_en_montee"; + String variable = "Nombre d'individus"; + String codeVariable = "nombre_d_individus"; String component = "unit"; + return referencesValues.get("variables_et_unites_par_types_de_donnees") + .findAll{it.get("nom du type de données").equals(datatype)} + .find{it.get("nom de la variable").equals(codeVariable)} + .get("nom de l'unité").equals((String)datum.variable.component); + references: + - variables_et_unites_par_types_de_donnees + format: + headerLine: 4 + firstRowLine: 5 + columns: + - header: projet + boundTo: + variable: projet + component: value + - header: site + boundTo: + variable: site + component: bassin + - header: plateforme + boundTo: + variable: site + component: plateforme + - header: date + boundTo: + variable: date + component: value + - header: espece + boundTo: + variable: espece + component: value + - header: Couleur des individus + boundTo: + variable: Couleur des individus + component: value + - header: Nombre d'individus + boundTo: + variable: Nombre d'individus + component: value + authorization: + authorizationScopes: + projet: + internationalizationName: + fr: Projet + en: Project + variable: projet + component: value + localization: + internationalizationName: + fr: Localisation + en: Localization + variable: site + component: chemin + timeScope: + variable: date + component: value + dataGroups: + referentiel: + internationalizationName: + fr: Référentiels + en: Repositories + label: Référentiel + data: + - projet + - site + - date + - espece + qualitatif: + internationalizationName: + fr: Qualitatif + en: Qualitative + label: Données qualitatives + data: + - Couleur des individus + quantitatif: + internationalizationName: + fr: Quantitatif + en: Quantitative + label: Données quantitatives + data: + - Nombre d'individus + uniqueness: + - variable: projet + component: value + - variable: site + component: chemin + - variable: site + component: plateforme + - variable: date + component: value + - variable: espece + component: value + test: + tags: [__hidden__] + internationalizationName: + fr: Test + en: Test + data: + projet: + tags: [test] + components: + value: + tags: [test] + checker: + name: Reference + params: + refType: projet + format: + headerLine: 4 + firstRowLine: 5 + columns: + - header: projet + boundTo: + variable: projet + component: value + authorization: + authorizationScopes: + projet: + internationalizationName: + fr: Projet + en: Project + variable: projet + component: value diff --git a/src/test/resources/http/multiplicity.yaml b/src/test/resources/http/multiplicity.yaml new file mode 100644 index 0000000000000000000000000000000000000000..418c2fd0651b0d684310ba7ac6c93e0aee8ccf39 --- /dev/null +++ b/src/test/resources/http/multiplicity.yaml @@ -0,0 +1,134 @@ +version: 1 +application: + defaultLanguage: fr + internationalizationName: + fr: Test de valeurs multiples + en: Multiple values test + name: multiplicity + version: 1 +references: + reference1: + keyColumns: [groupe,equipe] + columns: + groupe: + equipe: + projets: + checker: + name: Integer + params: + required: true + multiplicity: "MANY" + names: + checker: + name: String + params: + required: true + multiplicity: "MANY" + duration: + checker: + name: Float + params: + required: false + multiplicity: "MANY" + dates: + checker: + name: Date + params: + pattern: dd/MM/yyyy + required: false + multiplicity: "MANY" + reference2: + keyColumns: [groupe] + columns: + groupe: + computedColumns: + reference1: + checker: + name: Reference + params: + multiplicity: MANY + refType: reference1 + computation: + expression: > + String groupe = (String)datum["groupe"]; + references.reference1 + .findAll({it.refValues.groupe.equals(groupe)}) + .collect({it.naturalKey}) + .join(","); + references: + - reference1 +dataTypes: + bugs: + data: + bug: + components: + no: + checker: + name: Integer + params: + required: true + multiplicity: "MANY" + fichiers: + checker: + name: String + params: + pattern: .* + required: true + multiplicity: "MANY" + dates: + checker: + name: Date + params: + pattern: dd/MM/yyyy + required: false + multiplicity: "MANY" + projets: + checker: + name: Integer + params: + required: true + multiplicity: "MANY" + durations: + checker: + name: Float + params: + required: false + multiplicity: "MANY" + reference1: + checker: + name: Reference + params: + multiplicity: MANY + refType: reference1 + + format: + headerLine: 1 + firstRowLine: 2 + columns: + - header: no + boundTo: + variable: bug + component: no + - header: fichiers + boundTo: + variable: bug + component: fichiers + - header: dates + boundTo: + variable: bug + component: dates + - header: projets + boundTo: + variable: bug + component: projets + - header: durations + boundTo: + variable: bug + component: durations + - header: reference1 + boundTo: + variable: bug + component: reference1 + uniqueness: + - variable: bug + component: no \ No newline at end of file diff --git a/src/test/resources/http/postApplication.http b/src/test/resources/http/postApplication.http new file mode 100644 index 0000000000000000000000000000000000000000..e75b39f1485d15cf7c98b155e6f61c9e6a378967 --- /dev/null +++ b/src/test/resources/http/postApplication.http @@ -0,0 +1,24 @@ +### connexion +POST http://localhost:8081/api/v1/login?login=poussin&password=xxxx +Content-Type: application/json + +{ + "login": "poussin", + "password": "xxxx" +} + +### +POST http://localhost:8081/api/v1/applications/multiplicity +Content-Type: multipart/form-data; boundary=WebAppBoundary + +--WebAppBoundary +Content-Disposition: form-data; name="element-name" +Content-Type: text/plain + +Name +--WebAppBoundary +Content-Disposition: form-data; name="file"; filename="multiplicity.yaml" +Content-Type: application/x-yaml + +< ./multiplicity.yaml +--WebAppBoundary-- \ No newline at end of file diff --git a/ui/cypress/e2e/authorizationsReferences.cy.js b/ui/cypress/e2e/authorizationsReferences.cy.js index 7eaff66d9b113ad2f1fea8e6d5b163d003389050..d63fa2044e513e4ffbb18ec886d914caed812241 100644 --- a/ui/cypress/e2e/authorizationsReferences.cy.js +++ b/ui/cypress/e2e/authorizationsReferences.cy.js @@ -1585,7 +1585,7 @@ describe('test authorization references', () => { } }).as('pageRef') - cy.wait(60000); + cy.wait(6000); cy.visit(Cypress.env('ola_references_url')) cy.fixture(ola).then(olaContent => { @@ -11296,8 +11296,8 @@ describe('test authorization references', () => { cy.visit(Cypress.env('monsore_references_url')) cy.get('.button > :nth-child(1) > .icon').first().click() - cy.get(':nth-child(4) > .button').should('contain','Consulter les autorisations') - cy.get(':nth-child(4) > .button').should('be.disabled') + cy.get(':nth-child(5) > .button').should('contain','Consulter les autorisations') + cy.get(':nth-child(5) > .button').should('be.disabled') cy.screenshot() }) diff --git a/ui/cypress/e2e/createApplication.cy.js b/ui/cypress/e2e/createApplication.cy.js index 94c10e08c22fdc470417f927adf14ee188a10393..89b50d6db1a8f623bad6dfe045989eba395edeaa 100644 --- a/ui/cypress/e2e/createApplication.cy.js +++ b/ui/cypress/e2e/createApplication.cy.js @@ -76,7 +76,7 @@ describe('test create application', () => { }).as('getApplicationResponse') cy.screenshot() - cy.get('.buttons > .button > :nth-child(2)').first().click(); + cy.get('.columns button>:nth-child(2)').first().click(); cy.screenshot() cy.get('.columns').children().its('length').should('be.gt', 0) diff --git a/ui/src/components/common/CollapsibleInterval.vue b/ui/src/components/common/CollapsibleInterval.vue deleted file mode 100644 index 8228243f0cb483ce03d2b4923952d43647c2bd63..0000000000000000000000000000000000000000 --- a/ui/src/components/common/CollapsibleInterval.vue +++ /dev/null @@ -1,204 +0,0 @@ -<template> - <div> - <div> - <b-button - icon-left="plus" - outlined - type="is-dark is-light" - @click="changeIsCardModalActive(true)" - /> - <b-modal v-model="isCardModalActive" outlined scroll="keep"> - <div class="card"> - <header class="card-header is-align-content-center"> - <p v-if="variableComponent.type === 'numeric'" class="card-header-title"> - {{ $t("dataTypesManagement.title-modal-numeric") }} - </p> - <p v-if="variableComponent.type === 'date'" class="card-header-title"> - {{ $t("dataTypesManagement.title-modal-date") }} - </p> - </header> - <div class="rows"> - <div class="row"> - <div class="columns"> - <div class="column is-one-fifth"> - <label class="label">{{ $t("dataTypeAuthorizations.from") }}</label> - </div> - <div class="column is-four-fifth"> - <b-input - v-model="from" - :format="format" - :pattern="pattern" - :placeholder="format" - :type="inputType" - :validation-message="format" - /> - </div> - </div> - </div> - - <div class="row"> - <div class="columns"> - <div class="column is-one-fifth is-right"> - <label class="label">{{ $t("dataTypeAuthorizations.to") }}</label> - </div> - <div class="column is-four-fifth"> - <b-input - v-model="to" - :format="format" - :pattern="pattern" - :placeholder="format" - :type="inputType" - :validation-message="format" - label="et" - /> - </div> - </div> - </div> - <div class="row"> - <div class="columns"> - <div class="column is-4"></div> - <div class="column is-4"> - <b-button - expanded - icon-left="filter" - outlined - type="is-dark" - @click="submit" - ></b-button> - </div> - </div> - </div> - </div> - </div> - </b-modal> - </div> - </div> -</template> - -<script> -import { onMounted } from "vue"; -import useObject from "@/composable/components/object"; -import useBoolean from "@/composable/components/boolean"; -import useRegex from "@/composable/components/regex"; -import useText from "@/composable/components/text"; - -export default { - name: "CollapsibleInterval", - components: {}, - emits: ["setting_interval"], - props: { - variableComponent: Object, - }, - setup(props, ctx) { - const { refText: type, doChangeText: changeType } = useText(); - const { refText: format, doChangeText: changeFormat } = useText(); - const { refText: variable, doChangeText: changeVariable } = useText(); - const { refText: component, doChangeText: changeComponent } = useText(); - const { refText: key, doChangeText: changeKey } = useText(); - const { refBoolean: isCardModalActive, doChangeBoolean: changeIsCardModalActive } = - useBoolean(); - const { refText: from } = useText(); - const { refText: to } = useText(); - const { reactiveObject: dateTimeFormat } = useObject({ - d: { pattern: "\\d", type: "date" }, - h: { pattern: "\\d", type: "time" }, - m: { pattern: "\\d", type: "time" }, - s: { pattern: "\\d", type: "time" }, - n: { pattern: "\\d", type: "time" }, - a: { pattern: "[AP]M]", type: "time" }, - y: { pattern: "\\d", type: "date" }, - M: { pattern: "\\d", type: "date" }, - Z: { pattern: "[+-]\\d{4}", type: "date" }, - G: { pattern: "[AB]D", type: "date" }, - }); - const { refText: inputType, doChangeText: changeInputType } = useText("text"); - const { refRegex: pattern, doChangeRegex: changePattern } = useRegex(); - - onMounted(() => { - if (props.variableComponent) { - changeType(props.variableComponent.type); - changeFormat(props.variableComponent.format); - changeVariable(props.variableComponent.variable); - changeComponent(props.variableComponent.component); - changeKey(props.variableComponent.key); - } - let p = format.value; - let t = { date: false, time: false, isNumeric: false }; - if (type.value === "date") { - Object.keys(dateTimeFormat).forEach((search) => { - if (p.match(search)) { - t[dateTimeFormat[search].type] = true; - p = p.replaceAll(search, dateTimeFormat[search].pattern); - } - }); - changePattern("^" + p + "$"); - } - if (type.value === "numeric") { - if (format.value === "integer") { - t.isNumeric = true; - changePattern(/(?<=\s|^)[-+]?\d+(?=\s|$)/); - } - if (format.value === "float") { - t.isNumeric = true; - changePattern(/^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$/); - } - } - if (t.date && t.time) { - changeInputType("datetime"); - } else if (t.date) { - changeInputType("date"); - } else if (t.time) { - changeInputType("time"); - } else if (t.isNumeric) { - changeInputType("number"); - } - }); - - function submit() { - ctx.emit("setting_interval", { - type: type.value, - format: format.value, - key: key.value, - variable: variable.value, - component: component.value, - variableComponent: { - variable: variable.value, - component: component.value, - }, - intervalValues: { - from: from.value, - to: to.value, - }, - }); - changeIsCardModalActive(false); - } - - return { - submit, - changeIsCardModalActive, - inputType, - format, - pattern, - to, - from, - type, - isCardModalActive, - }; - }, -}; -</script> - -<style lang="scss" scoped> -.label { - text-align: right; - margin: 3px; -} - -.card { - margin: 1em; -} - -.column { - padding: 1.5em; -} -</style> diff --git a/ui/src/components/common/CollapsibleTree.vue b/ui/src/components/common/CollapsibleTree.vue index 010be3dae6ca310619cf9d9464a9021ab3e47991..7ab02f2a32f2c86b905abdb5efe315e61a586521 100644 --- a/ui/src/components/common/CollapsibleTree.vue +++ b/ui/src/components/common/CollapsibleTree.vue @@ -53,20 +53,12 @@ </span> </slot> <slot name="tags" v-bind:option="option"> - <div v-if="option.localtags" class="column"> - <span v-for="tag in option.localtags" :key="tag" style="margin-left: 5px"> - <b-tag v-if="tag !== 'no-tag'" class="is-primary is-light"> - {{ tag }} - </b-tag> - </span> - </div> - <div v-else-if="option.tags" class="column"> - <span v-for="tag in option.tags" :key="tag" style="margin-left: 5px"> - <b-tag v-if="tag !== 'no-tag'" class="is-primary is-light"> - {{ tag }} - </b-tag> - </span> - </div> + <TagsInfos + :tags-list-parent="option.localtags ? option.localtags : []" + :info-parent="option" + :tags-column="tags" + :tags-menu="true" + ></TagsInfos> </slot> </div> <LoadingAnimate v-if="isLoading" :size="'is-small'"></LoadingAnimate> @@ -208,10 +200,11 @@ import LoadingAnimate from "@/components/common/LoadingAnimate.vue"; import { lineCountSynthesis } from "@/composable/application/synthesis"; import { watch } from "vue"; import useBoolean from "@/composable/components/boolean"; +import TagsInfos from "@/components/common/TagsInfos.vue"; export default { name: "CollapsibleTree", - components: { LoadingAnimate, FontAwesomeIcon, AvailiblityChart }, + components: { TagsInfos, LoadingAnimate, FontAwesomeIcon, AvailiblityChart }, props: { applicationName: String, canUpload: { @@ -229,6 +222,9 @@ export default { default: false, }, }, + tags: { + type: Object, + }, referenceSynthesis: Array, onClickLabelCb: Function, onClickLabelSynthesisDetailCb: Function, diff --git a/ui/src/components/common/DetailsPanel.vue b/ui/src/components/common/DetailsPanel.vue index 0b5eda76b5064b6e3a4c523eced71e2bb8f540f7..a95f3c6cd0a8eb4193ab50996b4e7e953fd79263 100644 --- a/ui/src/components/common/DetailsPanel.vue +++ b/ui/src/components/common/DetailsPanel.vue @@ -5,7 +5,10 @@ :open="open" :title="refOrData && (refOrData.refNameLocal || refOrData.localName || refOrData.label)" > - <div v-if="refOrData.tags && refOrData.tags.length > 1" class="columns"> + <div + v-if="refOrData.tags && refOrData.tags.length > 0 && !refOrData.tags.includes('no-tag')" + class="columns" + > <caption> {{ $t("tags.tag") @@ -14,13 +17,7 @@ $t("ponctuation.colon") }} </caption> - <div v-for="tag in refOrData.tags" :key="tag" style="margin: 5px"> - <b-tag v-if="tags[tag].localName !== 'no-tag'" class="is-primary"> - <span> - {{ tags[tag].value.localName }} - </span> - </b-tag> - </div> + <TagsInfos :info-parent="refOrData" :tags-column="tags" :tags-menu="true"></TagsInfos> </div> <div v-if="reference" class="Panel-buttons"> <b-button @@ -59,6 +56,7 @@ import services from "@/composable/services"; import { i18n } from "@/main"; import useArray from "@/composable/components/array"; import { useRedirections } from "@/composable/applications/useFunction"; +import TagsInfos from "@/components/common/TagsInfos.vue"; export default { name: "ReferencesDetailsPanel", @@ -86,6 +84,7 @@ export default { canManageRights: Boolean, }, components: { + TagsInfos, SidePanel, }, setup(props) { diff --git a/ui/src/components/common/DropDownMenu.vue b/ui/src/components/common/DropDownMenu.vue index 9919095f8f41996157ad7d51ce97c5979743dacd..8ef3b672b34cc5acd4f4d206861800d69d89dc79 100644 --- a/ui/src/components/common/DropDownMenu.vue +++ b/ui/src/components/common/DropDownMenu.vue @@ -36,7 +36,6 @@ export default { }, methods: { select: function (option) { - console.log(this.option); this.$emit("select-menu-item", option || this.option); }, }, diff --git a/ui/src/components/common/FilterNumberOrDate.vue b/ui/src/components/common/FilterNumberOrDate.vue new file mode 100644 index 0000000000000000000000000000000000000000..15dad04a4dea9030684592b99ccf44eea2cb9b5f --- /dev/null +++ b/ui/src/components/common/FilterNumberOrDate.vue @@ -0,0 +1,236 @@ +<template> + <div> + <div v-if="modelValue.isInterval" class="rows"> + <InputDateInterval + v-if="type === 'date'" + :input-type="inputType" + :format="format" + :from="fromDate" + :to="toDate" + :is-simple-value="!modelValue.isInterval" + @update:dateValueInterval="submit($event)" + > + </InputDateInterval> + <InputNumberInterval + v-else-if="type === 'numeric'" + :input-type="inputType" + :format="format" + :from="fromNumber" + :to="toNumber" + :max="max" + :min="min" + :is-simple-value="!modelValue.isInterval" + @update:numberValueInterval="submit($event)" + > + </InputNumberInterval> + </div> + <InputNumber + v-else-if="type === 'numeric'" + :input-type="inputType" + :format="format" + :from="fromNumber" + :max="max" + :min="min" + :is-simple-value="!modelValue.isInterval" + @update:numberValue="submit($event)" + > + </InputNumber> + <InputDate + v-else-if="type === 'date'" + :input-type="inputType" + :format="format" + :from="fromDate" + :is-simple-value="!modelValue.isInterval" + @update:dateValue="submit($event)" + > + </InputDate> + </div> +</template> + +<script> +import { onMounted, watch } from "vue"; +import useObject from "@/composable/components/object"; +import useRegex from "@/composable/components/regex"; +import useText from "@/composable/components/text"; +import useNumber from "@/composable/components/number"; +import useDate from "@/composable/components/date"; +import useBoolean from "@/composable/components/boolean"; +import InputDate from "@/components/common/InputDate.vue"; +import InputDateInterval from "@/components/common/InputDateInterval.vue"; +import InputNumberInterval from "@/components/common/InputNumberInterval.vue"; +import InputNumber from "@/components/common/InputNumber.vue"; + +export default { + name: "FilterNumberOrDate", + components: { InputNumber, InputNumberInterval, InputDateInterval, InputDate }, + emits: ["update:modelValue"], + props: { + modelValue: Object, + }, + setup(props, ctx) { + watch( + () => props.modelValue, + (value) => { + const from = + (value.isInterval ? value?.value?.intervalValues?.from : value?.value?.simpleValue) || + null; + const to = + (value.isInterval ? value?.value?.intervalValues?.to : value?.value?.simpleValue) || null; + if (type.value === "date") { + fromDate.value = from; + toDate.value = to; + } else { + fromNumber.value = from; + toNumber.value = to; + } + } + ); + const { refText: type, doChangeText: changeType } = useText(); + const { refText: format, doChangeText: changeFormat } = useText(); + const { refText: variable, doChangeText: changeVariable } = useText(); + const { refText: component, doChangeText: changeComponent } = useText(); + const { refBoolean: isInterval, doChangeBoolean: changeIsInterval } = useBoolean(); + const { refText: key, doChangeText: changeKey } = useText(); + const { refText: from } = useText(); + const { refText: to } = useText(); + const { refDate: fromDate } = useDate(null); + const { refDate: toDate } = useDate(null); + const { refNumber: fromNumber } = useNumber(); + const { refNumber: toNumber } = useNumber(); + const { refNumber: min } = useNumber( + props.modelValue.checker.checkerDescription.params.min + ? props.modelValue.checker.checkerDescription.params.min + : -9999 + ); + const { refNumber: max } = useNumber( + props.modelValue.checker.checkerDescription.params.max + ? props.modelValue.checker.checkerDescription.params.max + : 9999 + ); + + const { reactiveObject: dateTimeFormat } = useObject({ + d: { pattern: "\\d", type: "date" }, + h: { pattern: "\\d", type: "time" }, + m: { pattern: "\\d", type: "time" }, + s: { pattern: "\\d", type: "time" }, + n: { pattern: "\\d", type: "time" }, + a: { pattern: "[AP]M]", type: "time" }, + y: { pattern: "\\d", type: "date" }, + M: { pattern: "\\d", type: "date" }, + Z: { pattern: "[+-]\\d{4}", type: "date" }, + G: { pattern: "[AB]D", type: "date" }, + }); + const { refText: inputType, doChangeText: changeInputType } = useText("text"); + const { refRegex: pattern, doChangeRegex: changePattern } = useRegex(); + + onMounted(() => { + if (props.modelValue) { + changeType(props.modelValue.type); + changeFormat( + props.modelValue.format + ? props.modelValue.format + : props.modelValue.checker.checkerDescription.params.pattern + ); + changeVariable(props.modelValue.variable); + changeComponent(props.modelValue.component); + changeIsInterval(props.modelValue.isInterval); + changeKey(props.modelValue.key); + } + let p = format.value; + let t = { date: false, time: false, isNumeric: false }; + if (type.value === "date") { + Object.keys(dateTimeFormat).forEach((search) => { + if (p.match(search)) { + t[dateTimeFormat[search].type] = true; + p = p.replaceAll(search, dateTimeFormat[search].pattern); + } + }); + changePattern("^" + p + "$"); + } + if (type.value === "numeric") { + if (format.value === "integer") { + t.isNumeric = true; + changePattern(/(?<=\s|^)[-+]?\d+(?=\s|$)/); + } + if (format.value === "float") { + t.isNumeric = true; + changePattern(/^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$/); + } + } + if (t.date && t.time) { + changeInputType("datetime"); + } else if (t.date) { + changeInputType("date"); + } else if (t.time) { + changeInputType("time"); + } else if (t.isNumeric) { + changeInputType("number"); + } + }); + + function submit(event) { + if (event && event.isSimpleValue) { + from.value = event.value; + } else if (event && event.interval) { + from.value = event.interval.from; + to.value = event.interval.to; + } + let value = props.modelValue.isInterval + ? { + intervalValues: { + from: from.value, + to: to.value, + }, + } + : { + simpleValue: from.value, + }; + ctx.emit("update:modelValue", { + ...props.modelValue, + type: type.value, + inputType: inputType.value, + format: format.value, + key: key.value, + variable: variable.value, + component: component.value, + variableComponent: { + variable: variable.value, + component: component.value, + }, + value, + isInterval: props.modelValue.isInterval ? props.modelValue.isInterval : isInterval.value, + }); + } + + return { + submit, + inputType, + format, + pattern, + to, + from, + type, + fromNumber, + toNumber, + toDate, + fromDate, + min, + max, + }; + }, +}; +</script> + +<style lang="scss" scoped> +.label { + margin: 3px; +} + +.card { + margin: 1em; +} + +.column { + padding: 1.5em; +} +</style> diff --git a/ui/src/components/common/FiltersCollapse.vue b/ui/src/components/common/FiltersCollapse.vue deleted file mode 100644 index 140a71078557123359a7bed633a054d34a73b87d..0000000000000000000000000000000000000000 --- a/ui/src/components/common/FiltersCollapse.vue +++ /dev/null @@ -1,87 +0,0 @@ -<template> - <b-collapse :open="open" animation="slide" class="card" style="margin: 10px"> - <template #trigger="props"> - <div class="card-header" role="button"> - <p class="card-header-title" style="text-transform: capitalize"> - {{ $t("applications.advancedFilter") }} - </p> - <a class="card-header-icon"> - <b-icon :icon="props.open ? 'chevron-up' : 'chevron-down'"></b-icon> - </a> - </div> - </template> - <div class="card-content" style="padding-bottom: 12px; padding-top: 12px"> - <div class="content columns is-multiline" style="margin-bottom: 10px"> - <div v-for="columns in columnsToBeShown" :key="columns.id"> - <b-field v-if="columns.id !== '#'" class="column" :label="columns.id"> - <b-input - v-model="filters[columns.id]" - :placeholder="$t('dataTypeAuthorizations.search')" - autocomplete="off" - class="is-primary" - icon="search" - type="search" - > - </b-input> - </b-field> - </div> - </div> - </div> - <div class="card-footer"> - <div class="card-footer-item"> - <b-button expanded icon-left="redo" outlined type="is-danger" @click="clear()" - >{{ $t("dataTypesManagement.réinitialiser") }} - {{ $t("dataTypesManagement.filtre") }} - </b-button> - </div> - <div class="card-footer-item"> - <b-button - expanded - icon-left="check" - outlined - type="is-dark" - @click="$emit('add-search', { filters })" - >{{ $t("dataTypesManagement.validate") }} - {{ $t("dataTypesManagement.filtre") }} - </b-button> - </div> - </div> - </b-collapse> -</template> - -<script> -import useArray from "@/composable/components/array"; -import useBoolean from "@/composable/components/boolean"; - -export default { - emits: ["add-search", "clear-search"], - name: "FiltersCollapse", - props: { - columnsToBeShown: { - type: Array, - defaults: [], - }, - }, - setup() { - const { shallowRefArray: filters, doChangeArray: changeFilters } = useArray(); - const { refBoolean: open } = useBoolean(); - - function clear() { - let listInput = document.getElementsByClassName("input"); - for (let i = 0; i < listInput.length; i++) { - listInput[i].value = ""; - } - changeFilters([]); - return this.$emit("clear-search", { filters }); - } - - return { - clear, - filters, - open, - }; - }, -}; -</script> - -<style scoped></style> diff --git a/ui/src/components/common/FiltersReferenceCollapse.vue b/ui/src/components/common/FiltersReferenceCollapse.vue new file mode 100644 index 0000000000000000000000000000000000000000..8c159d6d1d010f40dd3f38ac917f57a0f276272a --- /dev/null +++ b/ui/src/components/common/FiltersReferenceCollapse.vue @@ -0,0 +1,242 @@ +<template> + <b-collapse :open="open" animation="slide" class="card" style="margin: 10px"> + <template #trigger="props"> + <div class="card-header" role="button"> + <p class="card-header-title" style="text-transform: capitalize"> + {{ $t("applications.advancedFilter") }} + </p> + <a class="card-header-icon"> + <b-icon :icon="props.open ? 'chevron-up' : 'chevron-down'"></b-icon> + </a> + </div> + </template> + <div class="card-content" style="padding-bottom: 12px; padding-top: 12px"> + <div class="content columns is-multiline" style="margin-bottom: 10px"> + <div v-for="columns in columnsToBeShown" :key="columns.id"> + <b-field + v-if=" + (isRefLinkTo[columns.id] || + (application.columns[columns.id] && + application.columns[columns.id]?.checker && + application.columns[columns.id]?.checker?.name === 'Reference') || + (application.computedColumns[columns.id] && + application.computedColumns[columns.id]?.checker && + application.computedColumns[columns.id]?.checker?.name === 'Reference')) && + columns.id !== '#' + " + :label="columns.id" + class="column" + @click.native="filterListeReferenceValues(columns.id, columns.linkedTo)" + > + <b-autocomplete + v-model="filters[columns.id]" + :data="searchValueReference" + :placeholder="$t('dataTypeAuthorizations.search')" + class="is-primary" + icon="search" + open-on-focus + type="search" + @select=" + (option) => { + filters[columns.id] = option; + } + " + @keyup.native="filterListeReferenceValues(columns.id, columns.linkedTo)" + > + </b-autocomplete> + </b-field> + <b-field + v-else-if=" + columns.id !== '#' && + (application.computedColumns[columns.id] && + application.computedColumns[columns.id].checker && + application.computedColumns[columns.id]?.checker?.name !== 'String') + " + :label="columns.id" + class="column" + > + <InputNumber + v-if=" + application.computedColumns[columns.id].checker.name.toLowerCase() === 'float' || + application.computedColumns[columns.id].checker.name.toLowerCase() === 'integer' + " + :format="application.computedColumns[columns.id].checker.name.toLowerCase()" + :from="filters[columns.id]" + :is-simple-value="true" + @update:numberValue="updateValue(columns.id, $event)" + > + </InputNumber> + <InputDate + v-if=" + application.computedColumns[columns.id].checker.name.toLowerCase() === 'date' + " + :input-type="application.computedColumns[columns.id].checker.name.toLowerCase()" + :format="application.computedColumns[columns.id].checker.params.pattern" + :from="filters[columns.id]" + :is-simple-value="true" + @update:dateValue="updateValue(columns.id, $event)" + > + </InputDate> + </b-field> + <b-field + v-else-if=" + columns.id !== '#' && + application.columns[columns.id] && + application.columns[columns.id].checker && + application.columns[columns.id]?.checker?.name !== 'String' + " + :label="columns.id" + class="column" + > + <InputNumber + v-if=" + application.columns[columns.id].checker.name.toLowerCase() === 'float' || + application.columns[columns.id].checker.name.toLowerCase() === 'integer' + " + :format="application.columns[columns.id].checker.name.toLowerCase()" + :from="filters[columns.id]" + :is-simple-value="true" + @update:numberValue="updateValue(columns.id, $event)" + > + </InputNumber> + <InputDate + v-if="application.columns[columns.id].checker.name.toLowerCase() === 'date'" + :input-type="application.columns[columns.id].checker.name.toLowerCase()" + :format="application.columns[columns.id].checker.params.pattern" + :from="filters[columns.id]" + :is-simple-value="true" + @update:dateValue="updateValue(columns.id, $event)" + > + </InputDate> + </b-field> + <b-field v-else-if="columns.id !== '#'" :label="columns.id" class="column"> + <b-input + v-model="filters[columns.id]" + :placeholder="$t('dataTypeAuthorizations.search')" + autocomplete="off" + class="is-primary" + icon="search" + type="search" + > + </b-input> + </b-field> + </div> + </div> + </div> + <div class="card-footer"> + <div class="card-footer-item"> + <b-button expanded icon-left="redo" outlined type="is-danger" @click="clear()" + >{{ $t("dataTypesManagement.réinitialiser") }} + {{ $t("dataTypesManagement.filtre") }} + </b-button> + </div> + <div class="card-footer-item"> + <b-button + expanded + icon-left="check" + outlined + type="is-dark" + @click="$emit('add-search', { filters })" + >{{ $t("dataTypesManagement.validate") }} + {{ $t("dataTypesManagement.filtre") }} + </b-button> + </div> + </div> + </b-collapse> +</template> + +<script> +import useArray from "@/composable/components/array"; +import useBoolean from "@/composable/components/boolean"; +import services from "@/composable/services"; +import InputNumber from "@/components/common/InputNumber.vue"; +import InputDate from "@/components/common/InputDate.vue"; + +export default { + components: { InputDate, InputNumber }, + emits: ["add-search", "clear-search"], + name: "FiltersReferenceCollapse", + props: { + applicationName: { + type: String, + }, + application: { + type: Object, + }, + isRefLinkTo: { + type: Array, + defaults: [], + }, + columnsToBeShown: { + type: Array, + defaults: [], + }, + }, + setup(props) { + const { shallowRefArray: filters, doChangeArray: changeFilters } = useArray(); + const { refBoolean: open } = useBoolean(); + const displayLang = "__display_" + window.localStorage.lang; + const { shallowRefArray: searchValueReference, doChangeArray: changeSearchValueReference } = + useArray(); + + async function filterListeReferenceValues(columnsId, linkedTo) { + let valueReference = []; + let value = filters[columnsId]; + let reference; + changeSearchValueReference([]); + let refId = props.isRefLinkTo[columnsId]; + if (refId !== undefined) { + reference = await services.referenceService.getReferenceValues( + props.applicationName, + refId + ); + } else if (linkedTo !== undefined) { + reference = await services.referenceService.getReferenceValues( + props.applicationName, + linkedTo + ); + } + for (let i = 0; i < reference.referenceValues.length; i++) { + if (reference.referenceValues[i].values[displayLang]) { + valueReference.push(reference.referenceValues[i].values[displayLang]); + } else { + valueReference.push(reference.referenceValues[i].naturalKey); + } + } + if (value !== undefined) { + changeSearchValueReference( + valueReference.filter((option) => { + return option.toString().indexOf(value) >= 0; + }) + ); + } else { + changeSearchValueReference(valueReference); + } + } + + function clear() { + let listInput = document.getElementsByClassName("input"); + for (let i = 0; i < listInput.length; i++) { + listInput[i].value = ""; + } + changeFilters([]); + return this.$emit("clear-search", { filters }); + } + + function updateValue(columnsId, event) { + filters.value[columnsId] = event.value; + } + + return { + clear, + filterListeReferenceValues, + updateValue, + filters, + open, + searchValueReference, + }; + }, +}; +</script> + +<style scoped></style> diff --git a/ui/src/components/common/InputDate.vue b/ui/src/components/common/InputDate.vue new file mode 100644 index 0000000000000000000000000000000000000000..6895dab466b7356fcf4277e47a93afa9facba847 --- /dev/null +++ b/ui/src/components/common/InputDate.vue @@ -0,0 +1,73 @@ +<script> +import useDate from "@/composable/components/date"; +import { watch } from "vue"; + +export default { + name: "InputDate", + components: {}, + props: { + inputType: String, + format: String, + from: Date, + modelValue: Object, + isSimpleValue: Boolean, + }, + setup(props, ctx) { + const { refDate: valueDate } = useDate(null); + watch( + () => props.from, + (value) => { + valueDate.value = value; + } + ); + function updateValueDate() { + ctx.emit("update:dateValue", { + value: valueDate.value, + isSimpleValue: props.isSimpleValue, + }); + } + return { + valueDate, + updateValueDate, + }; + }, +}; +</script> + +<template> + <div v-if="inputType === 'datetime'" class="row"> + <b-datetimepicker + v-model="valueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datetimepicker> + </div> + <div v-else-if="inputType === 'date'" class="row"> + <b-datepicker + v-model="valueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datepicker> + </div> + <div v-else-if="inputType === 'time'" class="row"> + <b-timepicker + v-model="valueDate" + :placeholder="format" + editable + expanded + icon="clock" + @blur="updateValueDate" + > + </b-timepicker> + </div> +</template> + +<style scoped lang="scss"></style> diff --git a/ui/src/components/common/InputDateInterval.vue b/ui/src/components/common/InputDateInterval.vue new file mode 100644 index 0000000000000000000000000000000000000000..2842c14671a52e5f07fa61921ca0bdcdb7a9c579 --- /dev/null +++ b/ui/src/components/common/InputDateInterval.vue @@ -0,0 +1,124 @@ +<script> +import useDate from "@/composable/components/date"; +import { watch } from "vue"; + +export default { + name: "InputDateInterval", + components: {}, + props: { + inputType: String, + format: String, + from: Date, + to: Date, + modelValue: Object, + isSimpleValue: Boolean, + }, + setup(props, ctx) { + const { refDate: fromValueDate } = useDate(null); + const { refDate: toValueDate } = useDate(null); + watch( + () => props.from, + (value) => { + fromValueDate.value = value; + } + ); + watch( + () => props.to, + (value) => { + toValueDate.value = value; + } + ); + function updateValueDate() { + if (fromValueDate.value < toValueDate.value) { + ctx.emit("update:dateValueInterval", { + interval: { from: fromValueDate.value, to: toValueDate.value }, + }); + } + } + return { + fromValueDate, + toValueDate, + updateValueDate, + }; + }, +}; +</script> + +<template> + <div class="rows"> + <div class="row"> + <label class="label">{{ $t("dataTypeAuthorizations.from") }}</label> + </div> + <div v-if="inputType === 'datetime'" class="row"> + <b-datetimepicker + v-model="fromValueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datetimepicker> + </div> + <div v-else-if="inputType === 'date'" class="row"> + <b-datepicker + v-model="fromValueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datepicker> + </div> + <div v-else-if="inputType === 'time'" class="row"> + <b-timepicker + v-model="fromValueDate" + :placeholder="format" + editable + expanded + icon="clock" + @blur="updateValueDate" + > + </b-timepicker> + </div> + <div class="row"> + <label class="label">{{ $t("dataTypeAuthorizations.to") }}</label> + </div> + <div v-if="inputType === 'datetime'" class="row"> + <b-datetimepicker + v-model="toValueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datetimepicker> + </div> + <div v-else-if="inputType === 'date'" class="row"> + <b-datepicker + v-model="toValueDate" + :placeholder="format" + editable + expanded + icon="calendar-day" + @blur="updateValueDate" + > + </b-datepicker> + </div> + <div v-else-if="inputType === 'time'" class="row"> + <b-timepicker + v-model="toValueDate" + :placeholder="format" + editable + expanded + icon="clock" + @blur="updateValueDate" + > + </b-timepicker> + </div> + </div> +</template> + +<style scoped lang="scss"></style> diff --git a/ui/src/components/common/InputNumber.vue b/ui/src/components/common/InputNumber.vue new file mode 100644 index 0000000000000000000000000000000000000000..c2524045fbec9aff9cd08b54fdda8bb27264fc5a --- /dev/null +++ b/ui/src/components/common/InputNumber.vue @@ -0,0 +1,54 @@ +<script> +import { watch } from "vue"; +import useNumber from "@/composable/components/number"; + +export default { + name: "InputNumber", + components: {}, + props: { + inputType: String, + format: String, + from: Number, + min: Number, + max: Number, + modelValue: Object, + isSimpleValue: Boolean, + }, + setup(props, ctx) { + const { refNumber: valueNumber } = useNumber(); + watch( + () => props.from, + (value) => { + valueNumber.value = value; + } + ); + function updateValueNumber() { + ctx.emit("update:numberValue", { + value: valueNumber.value, + isSimpleValue: props.isSimpleValue, + }); + } + return { + valueNumber, + updateValueNumber, + }; + }, +}; +</script> + +<template> + <b-numberinput + v-model="valueNumber" + :max="max" + :min="min" + :placeholder="format" + controls-position="compact" + controls-rounded + :step="format === 'float' ? 0.1 : 1" + type="is-light" + @blur="updateValueNumber" + > + </b-numberinput> +</template> + +<style scoped lang="scss"></style> diff --git a/ui/src/components/common/InputNumberInterval.vue b/ui/src/components/common/InputNumberInterval.vue new file mode 100644 index 0000000000000000000000000000000000000000..c7aa9866aaf4823426115172f3aa5f58d2b1d1fd --- /dev/null +++ b/ui/src/components/common/InputNumberInterval.vue @@ -0,0 +1,84 @@ +<script> +import { watch } from "vue"; +import useNumber from "@/composable/components/number"; + +export default { + name: "InputNumberInterval", + components: {}, + props: { + inputType: String, + format: String, + from: Number, + to: Number, + min: Number, + max: Number, + modelValue: Object, + isSimpleValue: Boolean, + }, + setup(props, ctx) { + const { refNumber: fromValueNumber } = useNumber(); + const { refNumber: toValueNumber } = useNumber(); + watch( + () => props.from, + (value) => { + fromValueNumber.value = value; + } + ); + watch( + () => props.to, + (value) => { + toValueNumber.value = value; + } + ); + function updateValueNumber() { + if (fromValueNumber.value < toValueNumber.value) { + ctx.emit("update:numberValueInterval", { + interval: { from: fromValueNumber.value, to: toValueNumber.value }, + }); + } + } + return { + fromValueNumber, + toValueNumber, + updateValueNumber, + }; + }, +}; +</script> + +<template> + <div class="rows"> + <div class="row"> + <label class="label">{{ $t("dataTypeAuthorizations.from") }}</label> + </div> + <b-numberinput + v-model="fromValueNumber" + :max="max" + :min="min" + :placeholder="format" + controls-position="compact" + controls-rounded + :step="format === 'float' ? 0.1 : 1" + type="is-light" + @blur="updateValueNumber" + > + </b-numberinput> + <div class="row"> + <label class="label">{{ $t("dataTypeAuthorizations.to") }}</label> + </div> + <b-numberinput + v-model="toValueNumber" + :max="max" + :min="min" + :placeholder="format" + controls-position="compact" + controls-rounded + :step="format === 'float' ? 0.1 : 1" + type="is-light" + @blur="updateValueNumber" + > + </b-numberinput> + </div> +</template> + +<style scoped lang="scss"></style> diff --git a/ui/src/components/common/TagsInfos.vue b/ui/src/components/common/TagsInfos.vue new file mode 100644 index 0000000000000000000000000000000000000000..0263604edbabaa7e0e6a0d61b7858a9bbff75006 --- /dev/null +++ b/ui/src/components/common/TagsInfos.vue @@ -0,0 +1,54 @@ +<script> +export default { + name: "TagsInfos", + props: { + tagsListParent: { + type: Array, + }, + tagsColumn: { + type: Object, + }, + infoParent: { + type: Object, + }, + tagsMenu: { + type: Boolean, + default: false, + }, + }, +}; +</script> + +<template> + <div v-if="tagsMenu && tagsListParent"> + <span v-for="tag in tagsListParent" :key="tag" style="margin: 5px"> + <b-tag + v-if="tag !== 'no-tag' && Object.keys(tagsColumn).includes(tag)" + class="is-primary is-light" + > + <span> + {{ tagsColumn[tag].value.localName ? tagsColumn[tag].value.localName : tag }} + </span> + </b-tag> + </span> + </div> + <div v-else-if="tagsMenu && infoParent"> + <div v-for="tag in infoParent.tags" :key="tag" style="margin: 5px"> + <b-tag v-if="tagsColumn[tag].localName !== 'no-tag'" class="is-primary"> + <span> + {{ tagsColumn[tag].value.localName ? tagsColumn[tag].value.localName : tag }} + </span> + </b-tag> + </div> + </div> + <div v-else-if="!tagsMenu && Object.keys(tagsColumn).length !== 0 && infoParent"> + <span v-for="tag in infoParent.tags" :key="tag" style="margin-left: 5px"> + {{ infoParent.label ? infoParent.label : infoParent.title }} + <b-tag v-if="tag !== 'no-tag' && tagsColumn[tag].value" class="is-primary is-light"> + {{ tagsColumn[tag].value.localName ? tagsColumn[tag].value.localName : tag }} + </b-tag> + </span> + </div> +</template> + +<style scoped lang="scss"></style> diff --git a/ui/src/components/common/provider/OreInputNumber.vue b/ui/src/components/common/provider/OreInputNumber.vue index a19911605732140c3fb4fb54f20dc928993b5aa4..acd4b981a340816a49ee5a3e75556198ed399db9 100644 --- a/ui/src/components/common/provider/OreInputNumber.vue +++ b/ui/src/components/common/provider/OreInputNumber.vue @@ -22,7 +22,7 @@ </template> <b-taginput v-if="multiplicity === 'MANY'" - :placeholder="checker.name == 'IntegerType' ? '4' : 4.35" + :placeholder="checker.name === 'IntegerType' ? '4' : 4.35" v-model="val" :required="required" type="text" @@ -32,7 +32,7 @@ <b-input v-else v-model="val" - :placeholder="checker.name == 'IntegerType' ? '4' : 4.35" + :placeholder="checker.name === 'IntegerType' ? '4' : 4.35" :required="required" type="text" @blur="updateValue" diff --git a/ui/src/components/references/ReferencesLink.vue b/ui/src/components/references/ReferencesLink.vue index 408d0e7dfdf0ff44f553aeff4558842157049c53..375a7558e4d01f40c282cbcd9b6b55efae22a146 100644 --- a/ui/src/components/references/ReferencesLink.vue +++ b/ui/src/components/references/ReferencesLink.vue @@ -6,6 +6,9 @@ <LoadingAnimate v-if="isLoading" :size="'is-small'"></LoadingAnimate> </a> <p v-else-if="patternCheckerDate">{{ datePatternLang(value, patternCheckerDate) }}</p> + <p v-else-if="typeof value === 'string' && value.indexOf('date') === 0"> + {{ datePatternLang(value) }} + </p> <p v-else class="column">{{ value }}</p> <!-- modal de visualisation d'une donnée de référence --> diff --git a/ui/src/components/references/ReferencesManyLink.vue b/ui/src/components/references/ReferencesManyLink.vue index bcdb4ef4efc45b8290501aa12f141f5d7d69499c..8f4ef7853cee58fb811d72904139e92818f65039 100644 --- a/ui/src/components/references/ReferencesManyLink.vue +++ b/ui/src/components/references/ReferencesManyLink.vue @@ -8,14 +8,16 @@ :application="application" :column-id="val.columnName" :loaded-references-by-key="{}" - :reference-type="ReferenceType" + :reference-type="referenceType" :value="val" ></ReferencesLink> - <p v-else-if="val.indexOf('date:') !== -1">{{ /.{25}(.*$)/.exec(val)[1] }}</p> + <p v-else-if="typeof val === 'string' && val.indexOf('date') === 0"> + {{ datePatternLang(val) }} + </p> <p v-else class="column">{{ val }}</p> </div> </span> - <!-- modal de multiplicity et dynamique colonne --> + <!-- modal de dynamique colonne --> <b-modal v-model="isCardModalActive" class="modalCardRef" width="70%"> <div class="card"> <div class="card-header"> @@ -55,6 +57,7 @@ import { ReferenceService } from "@/services/rest/ReferenceService"; import ReferencesLink from "@/components/references/ReferencesLink.vue"; import ReferencesDynamicLink from "@/components/references/ReferencesDynamicLink.vue"; +import { datePatternLang } from "../../composable/application/DatePattern"; export default { name: "ReferencesManyLink", @@ -71,9 +74,6 @@ export default { }, multiplicity: Boolean, columnId: String, - // loadedReferencesById: { - // type: Object, - // }, }, beforeCreate() { this.$options.components.ReferencesDynamicLink = require("./ReferencesDynamicLink.vue").default; @@ -105,6 +105,7 @@ export default { }, }, methods: { + datePatternLang, async getReferenceValuesByKey(applicationName, referenceType, value) { return this.referenceService.getReferenceValuesByKey(applicationName, referenceType, value); }, @@ -160,7 +161,6 @@ export default { this.$emit("changedRefValues", { key: this.value, refType: this.referenceType, - // hierarchicalKey, refValues, }); return refValues; @@ -194,7 +194,6 @@ export default { this.modalArrayObj[j] = { ...this.modalArrayObj[j], column: column, value: value }; } } - console.log(this.modalArrayObj); /*return this.modalArrayObj;*/ }, }, diff --git a/ui/src/composable/application/DatePattern.js b/ui/src/composable/application/DatePattern.js index 713a34a16ae2def7aced3c533a1a73439900c551..149c1acff0df7987aa8f88cb189e51ebe1e1699a 100644 --- a/ui/src/composable/application/DatePattern.js +++ b/ui/src/composable/application/DatePattern.js @@ -31,6 +31,36 @@ export function datePatternLang(date, pattern) { } } +export function datePatternFormated(date, pattern) { + if (pattern != null && date != null) { + let year = date.toLocaleString("default", { year: "numeric" }); + let hour = date.toLocaleString("en-US", { hour: "2-digit", hour12: false }); + let minute = (date.getMinutes() < 10 ? "0" : "") + date.getMinutes(); + let second = (date.getSeconds() < 10 ? "0" : "") + date.getSeconds(); + pattern = pattern.replace("yyyy", year); + pattern = pattern.replace("HH", hour); + pattern = pattern.replace("mm", minute); + pattern = pattern.replace("ss", second); + if (pattern.includes("MM") || pattern.includes("dd")) { + let month = date.toLocaleString("default", { month: "2-digit" }); + pattern = pattern.replace("MM", month); + } else { + let month = date.toLocaleString("default", { month: "numeric" }); + pattern = pattern.replace("M", month); + } + if (pattern.includes("dd")) { + let day = date.toLocaleString("default", { day: "2-digit" }); + pattern = pattern.replace("dd", day); + } else { + let day = date.toLocaleString("default", { day: "numeric" }); + pattern = pattern.replace("d", day); + } + return pattern; + } else { + return new Date(date).toLocaleDateString(localStorage.getItem("lang")); + } +} + export function patternCheckerDateRef(application, columnName, refId) { if ( application.configuration.references && diff --git a/ui/src/composable/components/date.js b/ui/src/composable/components/date.js new file mode 100644 index 0000000000000000000000000000000000000000..63d1c6874d4390b315943e4a059123fdd5380dee --- /dev/null +++ b/ui/src/composable/components/date.js @@ -0,0 +1,13 @@ +import { ref } from "vue"; + +function useDate(date = new Date()) { + const refDate = ref(date); + const doChangeDate = function (newDate) { + refDate.value = new Date(newDate); + }; + return { + refDate, + doChangeDate, + }; +} +export default useDate; diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index 9d05bf38fc5da40989653fdfbd6b7e8828229b5e..da6b17ea1264e6528bae4e3b1e7885e0c54ace16 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -195,6 +195,7 @@ "close": "Requests validated", "open": "Requests not processed" }, + "interval": "Search for interval", "from": "from", "from-date": "From date : ", "from-date-to-date": "From date to date : ", diff --git a/ui/src/locales/fr.json b/ui/src/locales/fr.json index 146e1f5a824a239cb82ea585ed2927442d17ce6d..adbf67f8ff62e8ea2e5acca6b81c5fdc86d0882f 100644 --- a/ui/src/locales/fr.json +++ b/ui/src/locales/fr.json @@ -195,6 +195,7 @@ "close": "Requêtes validées", "open": "Requêtes non traîtées" }, + "interval": "Recherche par interval", "from": "de", "from-date": "À partir du", "from-date-to-date": "De date à date", diff --git a/ui/src/model/application/DownloadDatasetQuery.js b/ui/src/model/application/DownloadDatasetQuery.js index c136fd8891150c257ad1ae62df7291d6101c56ea..42d70ef143b2531cd9cd83d82013cce6e4991003 100644 --- a/ui/src/model/application/DownloadDatasetQuery.js +++ b/ui/src/model/application/DownloadDatasetQuery.js @@ -1,12 +1,7 @@ -import { Application } from "@/model/Application"; import { VariableComponentFilters } from "./VariableComponentFilters"; import { VariableComponentOrderBy } from "./VariableComponentOrderBy"; export class DownloadDatasetQuery { - application = new Application({}); - applicationNameOrId; - dataType; - reference; offset = 0; limit = 10; variableComponentSelects = []; @@ -15,9 +10,6 @@ export class DownloadDatasetQuery { authorizationDescriptions = []; constructor( downloadDatasetQueryOrApplication, - applicationNameOrId, - dataType, - reference, offset = 0, limit = 10, variableComponentSelects, @@ -33,10 +25,6 @@ export class DownloadDatasetQuery { : null) ); } else { - this.variableComponentKey = downloadDatasetQueryOrApplication; - this.applicationNameOrId = applicationNameOrId; - this.dataType = dataType; - this.reference = reference; this.offset = offset ? offset : 0; this.limit = limit ? limit : 10; this.variableComponentSelects = []; diff --git a/ui/src/services/TagService.js b/ui/src/services/TagService.js index dafd5ec22ce5e24d2decccc8cb1922dfe581993d..6a6f8601c9683678cac01c85dcbfc40871a405f0 100644 --- a/ui/src/services/TagService.js +++ b/ui/src/services/TagService.js @@ -53,4 +53,21 @@ export class TagService extends Fetcher { document.getElementById("tagsCollapse").className = "column"; } } + + filterTags(columnsVariableComponentsToBeShown) { + let paramsSelect = []; + for (let i = 0; i < columnsVariableComponentsToBeShown.length; i++) { + if ( + columnsVariableComponentsToBeShown[i].variable && + columnsVariableComponentsToBeShown[i].component + ) { + paramsSelect.push({ + variable: columnsVariableComponentsToBeShown[i].variable, + component: columnsVariableComponentsToBeShown[i].component, + }); + } + } + console.log(paramsSelect); + return paramsSelect; + } } diff --git a/ui/src/services/rest/DataService.js b/ui/src/services/rest/DataService.js index 7e26700c18565201b82db9204ac0c9472ee6f113..06805fcdb9b53d8ee60d8a9e72632df45b582bc7 100644 --- a/ui/src/services/rest/DataService.js +++ b/ui/src/services/rest/DataService.js @@ -11,7 +11,7 @@ export class DataService extends Fetcher { } async getDataType(applicationName, dataTypeId, params) { - return this.get(`applications/${applicationName}/data/${dataTypeId}`, { + return this.get(`applications/${applicationName}/data/${dataTypeId}/json`, { downloadDatasetQuery: JSON.stringify(params), }); } diff --git a/ui/src/views/datatype/DataTypeTableView.vue b/ui/src/views/datatype/DataTypeTableView.vue index 220485c1747df7cdc9f9f93d0c700adb54639939..a98847131d54d7989271115e8fc4f931c3c88b54 100644 --- a/ui/src/views/datatype/DataTypeTableView.vue +++ b/ui/src/views/datatype/DataTypeTableView.vue @@ -374,24 +374,28 @@ class="content" style="margin-bottom: 10px" > - <b-field v-if="variable.id === component.variable" :label="component.component"> + <b-field v-if="variable.id === component.variable"> + <b-field + v-if="'date' === component.type || 'numeric' === component.type" + :label="component.component" + > + <b-switch + v-model="component.isInterval" + @input="(ii) => updateComponents({ ...component, isInterval: ii })" + >{{ $t("dataTypeAuthorizations.interval") }} + </b-switch> + </b-field> + </b-field> + <b-field v-if="variable.id === component.variable"> <b-field v-if="'date' === component.type || 'numeric' === component.type"> - <CollapsibleInterval - :variable-component="component" - @setting_interval="addVariableSearchs" - ></CollapsibleInterval> - <b-input - v-model="search[component.variable + '_' + component.component]" - :placeholder="$t('dataTypeAuthorizations.search')" - autocomplete="off" - class="is-primary" - icon="search" - type="search" - @blur="addVariableSearchs(component)" - ></b-input> + <FilterNumberOrDate + :model-value="component" + @update:modelValue="addVariableSearchs" + ></FilterNumberOrDate> </b-field> <b-field v-else-if="'reference' === component.type" + :label="component.component" @click.native="recalculate(component)" > <b-autocomplete @@ -402,7 +406,6 @@ icon="search" open-on-focus type="search" - @blur="addVariableSearchs(component)" @select=" (option) => { search[component.variable + '_' + component.component] = option; @@ -413,7 +416,7 @@ > </b-autocomplete> </b-field> - <b-field v-else> + <b-field v-else :label="component.component"> <b-input v-model="search[component.variable + '_' + component.component]" :placeholder="$t('dataTypeAuthorizations.search')" @@ -526,12 +529,11 @@ :variable="variable.id" > <div v-if="variable.tags" class="column" style="padding: 0px"> - <span v-for="tag in variable.tags" :key="tag" style="margin-left: 5px"> - {{ variable.label }} - <b-tag v-if="tag !== 'no-tag'" class="is-primary is-light"> - {{ tag }} - </b-tag> - </span> + <TagsInfos + :info-parent="variable" + :tags-column="Object.keys(tagsColumn).length !== 0 ? tagsColumn : {}" + > + </TagsInfos> </div> <div v-else class="column"> {{ variable.label }} @@ -546,12 +548,11 @@ :variable="comp.variable" > <div v-if="comp.tags" class="column" style="padding: 0px"> - <span v-for="tag in comp.tags" :key="tag" style="margin-left: 5px"> - {{ comp.label }} - <b-tag v-if="tag !== 'no-tag'" class="is-primary is-light"> - {{ tag }} - </b-tag> - </span> + <TagsInfos + :info-parent="comp" + :tags-column="Object.keys(tagsColumn).length !== 0 ? tagsColumn : {}" + > + </TagsInfos> </div> <div v-else class="column"> {{ comp.label }} @@ -597,7 +598,9 @@ v-if="testMultiplicity(component.variable, component.component)" :application="application" :column-id="getDisplay(row, component.variable, component.component)" - :info-values="getValuesRow(row[component.variable][component.component])" + :info-values=" + getValuesRow(row[component.variable][component.component], component) + " :loaded-references-by-key="{}" :multiplicity="true" :reference-type="getRefType(component.checker)" @@ -676,7 +679,6 @@ import PageView from "@/views/common/PageView.vue"; import AuthorizationScopesMenu from "@/components/datatype/AuthorizationScopesMenu.vue"; import { ApplicationResult } from "@/model/ApplicationResult"; import SubMenu, { SubMenuPath } from "@/components/common/SubMenu.vue"; -import CollapsibleInterval from "@/components/common/CollapsibleInterval.vue"; import { DownloadDatasetQuery } from "@/model/application/DownloadDatasetQuery"; import { VariableComponentFilters } from "@/model/application/VariableComponentFilters"; import { VariableComponentKey } from "@/model/application/VariableComponentKey"; @@ -697,7 +699,9 @@ import services from "@/composable/services"; import { i18n } from "@/main"; import app from "@/main"; import { buildTagsForVariables } from "@/composable/application/tags"; -import { datePatternLang } from "@/composable/application/DatePattern"; +import { datePatternFormated, datePatternLang } from "@/composable/application/DatePattern"; +import FilterNumberOrDate from "@/components/common/FilterNumberOrDate.vue"; +import TagsInfos from "@/components/common/TagsInfos.vue"; const authorizationDescriptionsModel = new Authorization(); export default { @@ -714,8 +718,9 @@ export default { }, }, components: { + TagsInfos, + FilterNumberOrDate, AuthorizationScopesMenu, - CollapsibleInterval, draggable, LoadingAnimate, PageView, @@ -737,9 +742,6 @@ export default { const { refBoolean: showSort, doChangeBoolean: changeShowSort } = useBoolean(); const { reactiveObject: params, doChangeObject: changeParams } = useObject( new DownloadDatasetQuery({ - application: null, - applicationNameOrId: props.applicationName, - dataType: props.dataTypeId, offset: 0, limit: 10, variableComponentSelects: [], @@ -756,6 +758,10 @@ export default { const { shallowRefArray: variables, doChangeArray: changeVariables } = useArray(); const { shallowRefArray: variableComponents, doChangeArray: changeVariableComponents } = useArray(); + const { + shallowRefArray: variableComponentsDefaultValues, + doChangeArray: changeVariableComponentsDefaultValues, + } = useArray(); const { shallowRefArray: variableComponentsListToSort, doChangeArray: changeVariableComponentsListToSort, @@ -786,6 +792,7 @@ export default { // dans addSearch() const { refBoolean: showAdvancedSearch } = useBoolean(); + const { reactiveObject: isInterval } = useObject(); const { shallowRefArray: variableSearch, doChangeArray: changeVariableSearch, @@ -840,6 +847,23 @@ export default { changeIsLoading(true); changeTagsColumn(event); await initDatatype(); + changeVariables(services.tagService.toBeShown(tagsColumn.value, variables.value)); + changeVariableComponents( + variables.value + .map((v) => { + return Object.values(v.components).map((c) => + Object.assign(c, { + variable: v.label, + component: c.id, + isInterval: c.isInterval, + }) + ); + }) + .flat() + ); + params.variableComponentSelects = services.tagService.filterTags( + columnsVariableComponentsToBeShown.value + ); changeIsLoading(false); } @@ -849,6 +873,23 @@ export default { tagsColumn.value[key].value.selected = allTags; } await initDatatype(); + changeVariables(services.tagService.toBeShown(tagsColumn.value, variables.value)); + changeVariableComponents( + variables.value + .map((v) => { + return Object.values(v.components).map((c) => + Object.assign(c, { + variable: v.label, + component: c.id, + isInterval: c.isInterval, + }) + ); + }) + .flat() + ); + params.variableComponentSelects = services.tagService.filterTags( + columnsVariableComponentsToBeShown.value + ); changeIsLoading(false); } @@ -870,6 +911,7 @@ export default { ), }); await initDatatype(); + changeVariableComponents(variableComponentsDefaultValues.value); } async function initDatatype() { @@ -899,16 +941,16 @@ export default { return { rowId: r.rowId, ...r.values }; }) ); - const variablesModels = application.dataTypes[props.dataTypeId].variables; changeVariables(dataTypes.variables.map((v) => variablesModels[v])); - changeVariableComponents( + changeVariableComponentsDefaultValues( variables.value .map((v) => { return Object.values(v.components).map((c) => Object.assign(c, { variable: v.label, component: c.id, + isInterval: c.isInterval, ...getVariableComponentInfos(v.label, c.id, dataTypes), }) ); @@ -1229,25 +1271,27 @@ export default { } function getRefType(checkerRow) { - if (checkerRow?.configuration === null) { - if (checkerRow?.ReferenceType?.refType) { - return checkerRow.ReferenceType.refType; - } + if (checkerRow?.underlyingType?.refType !== undefined) { + return checkerRow.underlyingType.refType; } } - function getValuesRow(valuesRow) { - const rowValues = []; - const tableValuesRow = valuesRow.substring(1, valuesRow.length - 1).split(","); - for (let i = 0; i < tableValuesRow.length; i++) { - if (tableValuesRow[i].includes("/") && tableValuesRow[i].indexOf("date:") === -1) { - rowValues.push(tableValuesRow[i].slice(0, -1)); - } else if (tableValuesRow[i].includes('"')) { - rowValues.push(tableValuesRow[i].replaceAll('"', "")); + function getValuesRow(valuesRow, variableComponent) { + let newValues = []; + if (typeof valuesRow === "object") { + for (let i = 0; i < valuesRow.length; i++) { + if ( + typeof valuesRow[i] === "string" && + valuesRow[i].includes("/") && + valuesRow[i].indexOf("date:") === 0 + ) { + let pattern = variableComponent.checker.checkerDescription.params.pattern; + newValues.push(datePatternLang(valuesRow[i], pattern)); + } } } - if (rowValues.length !== 0) return rowValues; - else return tableValuesRow; + if (newValues.length !== 0) return newValues; + else return valuesRow; } function testMultiplicity(variable, component) { @@ -1315,9 +1359,12 @@ export default { let valueReference = []; let value = search[key]; changeSearchValueReference([]); - let refId = - application.dataTypes[props.dataTypeId].variables[variable].components[component].checker - .fieldType.refType; + let refId = application.dataTypes[props.dataTypeId].variables[variable].components[component] + .checker.fieldType.fieldType + ? application.dataTypes[props.dataTypeId].variables[variable].components[component].checker + .fieldType.fieldType.refType + : application.dataTypes[props.dataTypeId].variables[variable].components[component].checker + .fieldType.refType; let reference = await services.referenceService.getReferenceValues( props.applicationName, refId @@ -1359,47 +1406,82 @@ export default { return icon ? icon : null; } - async function addVariableSearchs(variableComponent, option) { - let { key, variable, component, type, format } = variableComponent; + function updateComponents(newComponent) { + variableComponents.value = variableComponents.value.map((component) => { + if (component.key === newComponent.key) { + return newComponent; + } else { + return component; + } + }); + } + + async function addVariableSearchs(newComponent, option) { + let { key, variable, component, type, format } = newComponent; let value = []; + let letSearch = null; + updateComponents(newComponent); let isRegExp = params.variableComponentFilters.isRegex; if (option) { search[key] = option; } value[key] = search[key]; - params.variableComponentFilters = params.variableComponentFilters.filter( - (c) => - c.variableComponentKey.variable !== variable || - c.variableComponentKey.component !== component - ); - let letSearch = null; - if (variableComponent.intervalValues) { + if (newComponent?.value?.simpleValue) { + if (newComponent.type === "date") { + search[key] = datePatternFormated(newComponent.value.simpleValue, newComponent.format); + } else { + search[key] = newComponent.value.simpleValue; + } letSearch = new VariableComponentFilters({ variableComponentKey: new VariableComponentKey({ variable: variable, component: component, }), + filter: search[key], type: type, format: format, isRegExp: isRegExp, - intervalValues: variableComponent.intervalValues, + }); + } else if (newComponent?.value?.intervalValues) { + if (newComponent.type === "date") { + search[key] = { + from: datePatternFormated(newComponent.value.intervalValues.from, newComponent.format), + to: + newComponent.value.intervalValues.to !== "" + ? datePatternFormated(newComponent.value.intervalValues.to, newComponent.format) + : "", + }; + } else { + search[key] = newComponent.value.intervalValues; + } + letSearch = new VariableComponentFilters({ + variableComponentKey: new VariableComponentKey({ + variable: variable, + component: component, + }), + type: type, + format: format, + isRegExp: isRegExp, + intervalValues: search[key], ...(letSearch ? new IntervalValues(letSearch) : {}), }); - } else if (value[key] && value[key].length > 0) { + } else if ((value[key] && value[key].length > 0) || value[key] !== 0 || value[key] !== null) { if ( - application.dataTypes[props.dataTypeId].variables[variable].components[component] - .checker !== null + newComponent.checker !== null && + newComponent.checker.fieldType && + newComponent.checker.fieldType.refType ) { - let refId = - application.dataTypes[props.dataTypeId].variables[variable].components[component] - .checker.fieldType.refType; + let refId = newComponent.checker.fieldType.refType; let reference = await services.referenceService.getReferenceValues( props.applicationName, refId ); for (let i = 0; i < reference.referenceValues.length; i++) { - if (reference.referenceValues[i].values[displayLang] === value[key]) { - value[key] = reference.referenceValues[i].naturalKey; + if ( + reference.referenceValues[i].values[displayLang] === value[key] || + reference.referenceValues[i].naturalKey === value[key] + ) { + value[key] = reference.referenceValues[i].hierarchicalKey; } } } @@ -1414,7 +1496,20 @@ export default { isRegExp: isRegExp, }); } - if (letSearch) { + params.variableComponentFilters = params.variableComponentFilters.filter( + (c) => + c.variableComponentKey.variable !== variable || + c.variableComponentKey.component !== component + ); + if ( + (letSearch && !newComponent?.value?.intervalValues) || + (letSearch && + newComponent?.value?.intervalValues && + newComponent?.value?.intervalValues.to !== "") + ) { + if (variableSearch.value.length === 1 && variableSearch.value[0].filter === null) { + changeVariableSearch([]); + } if (variableSearch.value.length === 0) { addVariableSearch(letSearch); } else { @@ -1432,33 +1527,22 @@ export default { } async function clearSearch() { + changeIsLoading(true); if (showFilter.value) { - changeParams( - new DownloadDatasetQuery({ - application: null, - applicationNameOrId: props.applicationName, - dataType: props.dataTypeId, - offset: 0, - limit: 15, - variableComponentSelects: params.variableComponentSelects, - variableComponentFilters: params.variableComponentFilters, - variableComponentOrderBy: params.variableComponentOrderBy, - authorizationDescriptions: [], - }) - ); + params.authorizationDescriptions = []; changeAuthorizationScopesMenusCount(1); changeAuthorizationDescriptions([authorizationDescriptionsModel]); } else { for (let key in search) { search[key] = ""; } - for (let i = 0; i < variableSearch.value.length; i++) { - params.variableComponentFilters = []; - changeVariableSearch([]); - changeSearchValueReference([]); - } + search.value = []; + params.variableComponentFilters = []; + changeVariableSearch([]); + changeSearchValueReference([]); } await initDatatype(); + changeVariableComponents(variableComponentsDefaultValues.value); } function clearOrder() { @@ -1471,7 +1555,7 @@ export default { function getDisplay(row, variable, component) { const key = variable + "_" + component; const value = row[variable][component]; - if (referenceLineCheckers.value[key]) { + if (referenceLineCheckers.value[key] && typeof value !== "object") { if ( referenceLineCheckers.value[key].referenceValues && referenceLineCheckers.value[key].referenceValues.refValues @@ -1483,7 +1567,11 @@ export default { return display ? display : value; } } - return value; + if (typeof value === "object") { + return "null"; + } else { + return value; + } } async function downloadResultSearch() { @@ -1597,6 +1685,7 @@ export default { recalculate, getSortIcon, addVariableSearchs, + updateComponents, downloadResultSearch, clearSearch, clearOrder, @@ -1607,6 +1696,7 @@ export default { newTableWithFilterTags, changeAllValueTags, rows, + isInterval, totalRows, referenceLineCheckers, search, diff --git a/ui/src/views/datatype/DataTypesManagementView.vue b/ui/src/views/datatype/DataTypesManagementView.vue index 38302ee6b45616dafb1d49d788865b11206ae02f..ccdbc545c80daca5f386d7b86cc2838c88f705fb 100644 --- a/ui/src/views/datatype/DataTypesManagementView.vue +++ b/ui/src/views/datatype/DataTypesManagementView.vue @@ -45,6 +45,7 @@ :is-loading="isSynthesisLoading" :is-uploading="isUploading" :level="0" + :tags="tags" :on-click-label-cb="(event, label) => openDataTypeCb(event, label)" :on-click-label-synthesis-detail-cb=" (event, option) => openDataTypeDetailSynthesisCb(event, option) @@ -148,7 +149,6 @@ export default { newListDataTypesWithFilterTags(); } - async function newListDataTypesWithFilterTags() { isSynthesisLoading.value = true; await init(); diff --git a/ui/src/views/references/ReferenceTableView.vue b/ui/src/views/references/ReferenceTableView.vue index 426157a970672fb4a8f948a4fcd5dd4190409a4b..f27ea9f852c069a96bc4a1f307786970c159331c 100644 --- a/ui/src/views/references/ReferenceTableView.vue +++ b/ui/src/views/references/ReferenceTableView.vue @@ -17,8 +17,11 @@ @change:allTags="changeAllValueTags($event)" /> </div> - <FiltersCollapse + <FiltersReferenceCollapse :columns-to-be-shown="columnsToBeShown" + :is-ref-link-to="referenceTypeForReferencingColumns" + :application-name="applicationName" + :application="application.configuration?.references[refId]" @add-search="recalculate($event)" @clear-search="clear($event)" /> @@ -63,12 +66,11 @@ > <template v-slot:header> <div v-if="column.tags" class="column" style="padding: 0px"> - <span v-for="tag in column.tags" :key="tag" style="margin-left: 5px"> - {{ column.title }} - <b-tag v-if="tag !== 'no-tag'" class="is-primary is-light"> - {{ tag }} - </b-tag> - </span> + <TagsInfos + :info-parent="column" + :tags-column="Object.keys(tags).length !== 0 ? tags : {}" + > + </TagsInfos> </div> <div v-else class="column"> {{ column.title }} @@ -160,9 +162,10 @@ import PageView from "../common/PageView.vue"; import { buildTagsColumns } from "@/composable/application/tags"; import { checkMessageErrors } from "@/composable/application/errors"; import { ApplicationResult } from "@/model/ApplicationResult"; -import FiltersCollapse from "@/components/common/FiltersCollapse.vue"; +import FiltersReferenceCollapse from "@/components/common/FiltersReferenceCollapse.vue"; import { init } from "@cypress/webpack-dev-server/dist/aut-runner"; import { patternCheckerDateRef } from "@/composable/application/DatePattern"; +import TagsInfos from "@/components/common/TagsInfos.vue"; export default { name: "ReferenceTableView", @@ -172,7 +175,8 @@ export default { refId: String, }, components: { - FiltersCollapse, + TagsInfos, + FiltersReferenceCollapse, LoadingAnimate, PageView, SubMenu, @@ -206,10 +210,7 @@ export default { const { shallowRefArray: tableValues, doChangeArray: changeTableValues } = useArray(); const { shallowRefArray: tags, doChangeArray: changeTags } = useArray(); const { shallowRefArray: filters, doAddToArray: pushFilters } = useArray(); - const { - shallowRefArray: referenceTypeForReferencingColumns, - doChangeArray: changeReferenceTypeForReferencingColumns, - } = useArray(); + const { shallowRefArray: referenceTypeForReferencingColumns } = useArray(); const { reactiveObject: referencesDynamic, doChangeObject: changeReferencesDynamic } = useObject(); const columnsToBeShown = computed(() => @@ -278,7 +279,12 @@ export default { ); if (references) { changeReferenceValues(references.referenceValues); - changeReferenceTypeForReferencingColumns(references.referenceTypeForReferencingColumns); + if (Object.keys(references.referenceTypeForReferencingColumns).length > 0) { + for (let key in references.referenceTypeForReferencingColumns) { + referenceTypeForReferencingColumns.value[key] = + references.referenceTypeForReferencingColumns[key]; + } + } } for (let i = 0; i < application.referenceSynthesis.length; i++) { if (application.referenceSynthesis[i].ReferenceType === props.refId) { @@ -393,9 +399,9 @@ export default { } function addRefLinkedTo(idColumn, linkedTo) { - if (linkedTo === null && referenceTypeForReferencingColumns.value.length !== 0) { + if (linkedTo === null) { for (let key in referenceTypeForReferencingColumns.value) { - if (key === idColumn) { + if (key === idColumn && referenceTypeForReferencingColumns.value[key]) { return referenceTypeForReferencingColumns.value[key]; } } @@ -498,6 +504,7 @@ export default { newListColumnsWithFilterTags, clear, changeAllValueTags, + referenceTypeForReferencingColumns, tags, subMenuPaths, params, diff --git a/ui/src/views/references/ReferencesManagementView.vue b/ui/src/views/references/ReferencesManagementView.vue index 9e8b3ad0d62f101646988d21ae1bf2728c9eeea6..9755a92dc8a133724f10ad599cf8d444531420f2 100644 --- a/ui/src/views/references/ReferencesManagementView.vue +++ b/ui/src/views/references/ReferencesManagementView.vue @@ -46,6 +46,7 @@ :is-loading="isLineCountLoading" :is-uploading="isUploading" :level="0" + :tags="tags" :line-count="lineCountSynthesis(application.referenceSynthesis, ref)" :on-click-label-cb="(event, label) => openRefDetails(event, label)" :on-upload-cb="(label, refFile) => uploadReferenceCsv(label, refFile)"