diff --git a/.circleci/config.yml b/.circleci/config.yml index b6f0cb24d..b27d67287 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2516,7 +2516,7 @@ commands: orbs: maven: circleci/maven@1.0.1 openjdk-install: cloudesire/openjdk-install@1.2.3 - sonarcloud: sonarsource/sonarcloud@1.0.2 + sonarcloud: sonarsource/sonarcloud@2.0.0 executors: # https://hub.docker.com/u/cimg (circleci next-gen docker images) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0c61ec775..93a9bbf20 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,8 @@ updates: ignore: - dependency-name: "*" update-types: ["version-update:semver-major"] + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/.github/workflows/build-multiarch-image-latest.yml b/.github/workflows/build-multiarch-image-latest.yml index b39c0e823..81b0a7f6a 100644 --- a/.github/workflows/build-multiarch-image-latest.yml +++ b/.github/workflows/build-multiarch-image-latest.yml @@ -15,19 +15,19 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push (AMD64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64 @@ -35,7 +35,7 @@ jobs: tags: ehrbase/ehrbase:latest-amd64 - name: Build and push (ARM64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/arm64 diff --git a/.github/workflows/build-multiarch-image-next.yml b/.github/workflows/build-multiarch-image-next.yml index 1cef434db..aa174566b 100644 --- a/.github/workflows/build-multiarch-image-next.yml +++ b/.github/workflows/build-multiarch-image-next.yml @@ -31,19 +31,19 @@ jobs: # Set as Environment for all further steps echo "TAG=${v}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push (AMD64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64 @@ -51,7 +51,7 @@ jobs: tags: ehrbase/ehrbase:${{env.TAG}}-amd64 - name: Build and push (ARM64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/arm64 diff --git a/.github/workflows/build-multiarch-image-tag.yml b/.github/workflows/build-multiarch-image-tag.yml index d54fdff92..97af6f8b8 100644 --- a/.github/workflows/build-multiarch-image-tag.yml +++ b/.github/workflows/build-multiarch-image-tag.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Create TAG ENV from version of release Branch run: | @@ -27,16 +27,16 @@ jobs: echo "TAG=$TAG" >> $GITHUB_ENV - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push (AMD64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64 @@ -44,7 +44,7 @@ jobs: tags: ehrbase/ehrbase:tag-amd64 - name: Build and push (ARM64) - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . platforms: linux/arm64 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0bda9b39d..50afb708f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,12 +33,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' @@ -48,7 +48,7 @@ jobs: run: mvn -B verify - name: Setup Maven Central - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f37a46eb2..a136aaa05 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -53,10 +53,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' diff --git a/.github/workflows/jdk-compat-robot.yml b/.github/workflows/jdk-compat-robot.yml index f4a2c2b26..019e58934 100644 --- a/.github/workflows/jdk-compat-robot.yml +++ b/.github/workflows/jdk-compat-robot.yml @@ -36,12 +36,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: ${{ matrix.java }} @@ -69,7 +69,7 @@ jobs: cat /var/tmp/log.txt - name: Checkout robot - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 repository: ehrbase/integration-tests diff --git a/.github/workflows/jdk-compat.yml b/.github/workflows/jdk-compat.yml index 4451da9c6..5c2f312d5 100644 --- a/.github/workflows/jdk-compat.yml +++ b/.github/workflows/jdk-compat.yml @@ -28,12 +28,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: ${{ matrix.java }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a97109fed..9de8fb8cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: steps: # Install the sdk main in the local repo so maven version plugin can find them - name: Checkout SDK main - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 repository: ehrbase/openEHR_SDK @@ -33,7 +33,7 @@ jobs: run: mvn install -Dmaven.test.skip=true # Install the sdk dev in the local repo so maven version plugin can find them - name: Checkout SDK dev - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 repository: ehrbase/openEHR_SDK @@ -46,7 +46,7 @@ jobs: run: mvn install - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # This will be used by git in all further steps diff --git a/CHANGELOG.md b/CHANGELOG.md index 2144c585f..f4552ceb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.31.0] + ### Added + ### Changed + - Upgrade openEHR_SDK to version 2.3.0 see https://github.com/ehrbase/openEHR_SDK/blob/develop/CHANGELOG.md + - Migrated to spring boot 3 ([#1174](https://github.com/ehrbase/ehrbase/pull/1174)) + - Removed authorization scopes from endpoints and added support for overwriting controllers ([#1157](https://github.com/ehrbase/ehrbase/pull/1157)) + ### Fixed + - Fix audit logs location ([#1160](https://github.com/ehrbase/ehrbase/pull/1160)) + - Address AQL query security vulnerabilities ([#1190](https://github.com/ehrbase/ehrbase/pull/1190)) + ## [0.30.0] ### Added ### Changed @@ -685,3 +695,4 @@ the next release this file will provide a proper overview. [0.28.0]: https://github.com/ehrbase/ehrbase/compare/v0.27.4...v0.28.0 [0.29.0]: https://github.com/ehrbase/ehrbase/compare/v0.28.0...v0.29.0 [0.30.0]: https://github.com/ehrbase/ehrbase/compare/v0.29.0...v0.30.0 +[0.31.0]: https://github.com/ehrbase/ehrbase/compare/v0.30.0...v0.31.0 diff --git a/api/pom.xml b/api/pom.xml index 458fa405f..923e2d544 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -26,7 +26,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 api diff --git a/api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorizations.java b/api/src/main/java/org/ehrbase/api/annotations/EhrbaseSecurity.java similarity index 78% rename from api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorizations.java rename to api/src/main/java/org/ehrbase/api/annotations/EhrbaseSecurity.java index d2894cb88..3c929fda1 100644 --- a/api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorizations.java +++ b/api/src/main/java/org/ehrbase/api/annotations/EhrbaseSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School. + * Copyright (c) 2023 vitasystems GmbH and Hannover Medical School. * * This file is part of project EHRbase * @@ -15,18 +15,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.ehrbase.api.authorization; +package org.ehrbase.api.annotations; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; -@Inherited @Retention(RUNTIME) @Target(ElementType.METHOD) -public @interface EhrbaseAuthorizations { - EhrbaseAuthorization[] value(); -} +public @interface EhrbaseSecurity {} diff --git a/api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorization.java b/api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorization.java deleted file mode 100644 index b7e27105c..000000000 --- a/api/src/main/java/org/ehrbase/api/authorization/EhrbaseAuthorization.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School. - * - * This file is part of project EHRbase - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehrbase.api.authorization; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Repeatable; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Inherited -@Retention(RUNTIME) -@Repeatable(EhrbaseAuthorizations.class) -@Target(ElementType.METHOD) -public @interface EhrbaseAuthorization { - EhrbasePermission permission(); -} diff --git a/application/pom.xml b/application/pom.xml index 53d6e662d..07f10a93e 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -26,16 +26,18 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 application + jar vitasystems/hip-openehr + org.springframework.boot spring-boot-starter-web diff --git a/application/src/main/java/org/ehrbase/application/abac/CustomMethodSecurityExpressionRoot.java b/application/src/main/java/org/ehrbase/application/abac/CustomMethodSecurityExpressionRoot.java index 88bf2da66..b9d679bee 100644 --- a/application/src/main/java/org/ehrbase/application/abac/CustomMethodSecurityExpressionRoot.java +++ b/application/src/main/java/org/ehrbase/application/abac/CustomMethodSecurityExpressionRoot.java @@ -188,7 +188,7 @@ private boolean checkAbac(String type, String subject, Object payload, String co } // Check and extract JWT - var jwt = getJwtAuthenticationToken(this.authentication); + var jwt = getJwtAuthenticationToken(this.getAuthentication()); // Request body map. will result in simple JSON like {"patient_id":"...", ...} // but requires "Object" for template handling, which can have a Set for multiple IDs diff --git a/application/src/main/java/org/ehrbase/application/config/ServerConfigImp.java b/application/src/main/java/org/ehrbase/application/config/ServerConfigImp.java index 1ba494b8f..e09d2a8c0 100644 --- a/application/src/main/java/org/ehrbase/application/config/ServerConfigImp.java +++ b/application/src/main/java/org/ehrbase/application/config/ServerConfigImp.java @@ -17,8 +17,8 @@ */ package org.ehrbase.application.config; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; diff --git a/application/src/main/java/org/ehrbase/application/config/SwaggerConfiguration.java b/application/src/main/java/org/ehrbase/application/config/SwaggerConfiguration.java index 43f4f7d65..da0e7db60 100644 --- a/application/src/main/java/org/ehrbase/application/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/ehrbase/application/config/SwaggerConfiguration.java @@ -21,7 +21,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; -import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/application/src/main/java/org/ehrbase/application/config/security/BasicAuthSecurityConfiguration.java b/application/src/main/java/org/ehrbase/application/config/security/BasicAuthSecurityConfiguration.java index 30caf0395..28c2fdc9d 100644 --- a/application/src/main/java/org/ehrbase/application/config/security/BasicAuthSecurityConfiguration.java +++ b/application/src/main/java/org/ehrbase/application/config/security/BasicAuthSecurityConfiguration.java @@ -18,18 +18,22 @@ package org.ehrbase.application.config.security; import static org.ehrbase.application.config.security.SecurityProperties.ADMIN; -import static org.ehrbase.application.config.security.SecurityProperties.USER; +import static org.springframework.security.config.Customizer.withDefaults; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; /** @@ -42,55 +46,43 @@ @Configuration @ConditionalOnProperty(prefix = "security", name = "authType", havingValue = "basic") @EnableWebSecurity -public class BasicAuthSecurityConfiguration extends WebSecurityConfigurerAdapter { +public class BasicAuthSecurityConfiguration { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final SecurityProperties properties; - - public BasicAuthSecurityConfiguration(SecurityProperties securityProperties) { - this.properties = securityProperties; - } - @PostConstruct public void initialize() { logger.info("Using basic authentication"); } - @Override - public void configure(AuthenticationManagerBuilder auth) throws Exception { - // @formatter:off - auth.inMemoryAuthentication() - .withUser(properties.getAuthUser()) - .password("{noop}" + properties.getAuthPassword()) - .roles(USER) - .and() - .withUser(properties.getAuthAdminUser()) - .password("{noop}" + properties.getAuthAdminPassword()) - .roles(ADMIN); - // @formatter:on + @Bean + public InMemoryUserDetailsManager inMemoryUserDetailsManager( + SecurityProperties properties, ObjectProvider passwordEncoder) { + + return new InMemoryUserDetailsManager( + User.withUsername(properties.getAuthUser()) + .password("{noop}" + properties.getAuthPassword()) + .roles(SecurityProperties.USER) + .build(), + User.withUsername(properties.getAuthAdminUser()) + .password("{noop}" + properties.getAuthAdminPassword()) + .roles(SecurityProperties.ADMIN) + .build()); } - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.addFilterBefore(new SecurityFilter(), BasicAuthenticationFilter.class); - // @formatter:off - http.cors() - .and() - .csrf() - .ignoringAntMatchers("/rest/**") - .and() - .authorizeRequests() - .antMatchers("/rest/admin/**", "/management/**") - .hasRole(ADMIN) - .anyRequest() - .hasAnyRole(ADMIN, USER) - .and() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .httpBasic(); - // @formatter:on + http.cors(withDefaults()) + .csrf(c -> c.ignoringRequestMatchers("/rest/**")) + .authorizeHttpRequests(auth -> auth.requestMatchers("/rest/admin/**", "/management/**") + .hasRole(ADMIN) + .anyRequest() + .hasAnyRole(ADMIN, SecurityProperties.USER)) + .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .httpBasic(withDefaults()); + + return http.build(); } } diff --git a/application/src/main/java/org/ehrbase/application/config/security/OAuth2SecurityConfiguration.java b/application/src/main/java/org/ehrbase/application/config/security/OAuth2SecurityConfiguration.java index 7dd490974..42335a9a5 100644 --- a/application/src/main/java/org/ehrbase/application/config/security/OAuth2SecurityConfiguration.java +++ b/application/src/main/java/org/ehrbase/application/config/security/OAuth2SecurityConfiguration.java @@ -17,12 +17,14 @@ */ package org.ehrbase.application.config.security; +import static org.ehrbase.application.config.security.SecurityProperties.ADMIN; +import static org.springframework.security.config.Customizer.withDefaults; + import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,17 +32,19 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; -import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter; +import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter; +import org.springframework.security.web.SecurityFilterChain; /** * {@link Configuration} for OAuth2 authentication. @@ -48,11 +52,10 @@ * @author Jake Smolka * @since 1.0.0 */ -@Deprecated @Configuration @EnableWebSecurity @ConditionalOnProperty(prefix = "security", name = "auth-type", havingValue = "oauth") -public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter { +public class OAuth2SecurityConfiguration { private static final String PUBLIC = "PUBLIC"; private static final String PRIVATE = "PRIVATE"; @@ -87,48 +90,43 @@ public void initialize() { logger.debug("Using admin role: {}", securityProperties.getOauth2AdminRole()); } - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { String userRole = securityProperties.getOauth2UserRole(); String adminRole = securityProperties.getOauth2AdminRole(); http.addFilterBefore(new SecurityFilter(), BearerTokenAuthenticationFilter.class); - // @formatter:off - var registry = http.cors() - .and() - .authorizeRequests() - .antMatchers("/rest/admin/**") - .hasRole(adminRole) - .antMatchers("/swagger-ui/**", "/v3/api-docs/**") - .permitAll(); - - var managementAuthorizedUrl = registry.and() - .authorizeRequests() - .antMatchers(this.managementWebEndpointProperties.getBasePath() + "/**"); - - switch (managementEndpointsAccessType) { - case ADMIN_ONLY -> - // management endpoints are locked behind an authorization - // and are only available for users with the admin role - managementAuthorizedUrl.hasRole(adminRole); - case PRIVATE -> - // management endpoints are locked behind an authorization, but are available to any role - managementAuthorizedUrl.hasAnyRole(adminRole, userRole, PROFILE_SCOPE); - case PUBLIC -> - // management endpoints can be accessed without an authorization - managementAuthorizedUrl.permitAll(); - default -> throw new IllegalStateException(String.format( - "Unexpected management endpoints access control type %s", managementEndpointsAccessType)); - } - - registry.anyRequest() - .hasAnyRole(adminRole, userRole, PROFILE_SCOPE) - .and() - .oauth2ResourceServer() - .jwt() - .jwtAuthenticationConverter(getJwtAuthenticationConverter()); - // @formatter:on + http.cors(withDefaults()) + .authorizeHttpRequests(auth -> { + AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry = + auth.requestMatchers("/rest/admin/**", "/management/**") + .hasRole(ADMIN); + + var managementAuthorizedUrl = + registry.requestMatchers(this.managementWebEndpointProperties.getBasePath() + "/**"); + + switch (managementEndpointsAccessType) { + case ADMIN_ONLY -> + // management endpoints are locked behind an authorization + // and are only available for users with the admin role + managementAuthorizedUrl.hasRole(adminRole); + case PRIVATE -> + // management endpoints are locked behind an authorization, but are available to any role + managementAuthorizedUrl.hasAnyRole(adminRole, userRole, PROFILE_SCOPE); + case PUBLIC -> + // management endpoints can be accessed without an authorization + managementAuthorizedUrl.permitAll(); + default -> throw new IllegalStateException(String.format( + "Unexpected management endpoints access control type %s", + managementEndpointsAccessType)); + } + + registry.anyRequest().hasAnyRole(adminRole, userRole, PROFILE_SCOPE); + }) + .oauth2ResourceServer(o -> o.jwt(j -> j.jwtAuthenticationConverter(getJwtAuthenticationConverter()))); + + return http.build(); } // Converter creates list of "ROLE_*" (upper case) authorities for each "realm access" role @@ -146,7 +144,7 @@ private Converter getJwtAuthenticationConverte .stream() .map(roleName -> "ROLE_" + roleName.toUpperCase()) .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList())); + .toList()); } if (jwt.getClaims().containsKey("scope")) { @@ -154,7 +152,7 @@ private Converter getJwtAuthenticationConverte Arrays.stream(jwt.getClaims().get("scope").toString().split(" ")) .map(roleName -> "ROLE_" + roleName.toUpperCase()) .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList())); + .toList()); } return authority; }); diff --git a/application/src/main/java/org/ehrbase/application/config/security/SecurityFilter.java b/application/src/main/java/org/ehrbase/application/config/security/SecurityFilter.java index 3427c0339..4618c0b65 100644 --- a/application/src/main/java/org/ehrbase/application/config/security/SecurityFilter.java +++ b/application/src/main/java/org/ehrbase/application/config/security/SecurityFilter.java @@ -17,8 +17,8 @@ */ package org.ehrbase.application.config.security; +import jakarta.servlet.*; import java.io.IOException; -import javax.servlet.*; import org.springframework.security.core.context.SecurityContextHolder; public class SecurityFilter implements Filter { diff --git a/application/src/main/java/org/ehrbase/application/config/web/AuditHandlerInterceptorDelegator.java b/application/src/main/java/org/ehrbase/application/config/web/AuditHandlerInterceptorDelegator.java index d7bd181fe..bae34fc20 100644 --- a/application/src/main/java/org/ehrbase/application/config/web/AuditHandlerInterceptorDelegator.java +++ b/application/src/main/java/org/ehrbase/application/config/web/AuditHandlerInterceptorDelegator.java @@ -17,8 +17,8 @@ */ package org.ehrbase.application.config.web; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.ehrbase.api.audit.interceptor.AuditInterceptor; import org.springframework.lang.Nullable; import org.springframework.web.servlet.HandlerInterceptor; diff --git a/application/src/main/java/org/ehrbase/application/config/web/WebConfiguration.java b/application/src/main/java/org/ehrbase/application/config/web/WebConfiguration.java index c7eb93f43..35d78bb48 100644 --- a/application/src/main/java/org/ehrbase/application/config/web/WebConfiguration.java +++ b/application/src/main/java/org/ehrbase/application/config/web/WebConfiguration.java @@ -24,6 +24,7 @@ import org.springframework.lang.NonNull; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** @@ -56,4 +57,9 @@ public void addCorsMappings(CorsRegistry registry) { public void addInterceptors(@NonNull InterceptorRegistry registry) { auditInterceptorHandler.registerAuditInterceptors(registry); } + + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.setUseTrailingSlashMatch(true); + } } diff --git a/application/src/main/java/org/ehrbase/application/web/LoggingContextFilter.java b/application/src/main/java/org/ehrbase/application/web/LoggingContextFilter.java index b01c53203..715c9fc31 100644 --- a/application/src/main/java/org/ehrbase/application/web/LoggingContextFilter.java +++ b/application/src/main/java/org/ehrbase/application/web/LoggingContextFilter.java @@ -17,12 +17,12 @@ */ package org.ehrbase.application.web; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.ThreadLocalRandom; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.slf4j.MDC; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; diff --git a/application/src/main/resources/application.yml b/application/src/main/resources/application.yml index 4a8eb86f7..d482723ff 100644 --- a/application/src/main/resources/application.yml +++ b/application/src/main/resources/application.yml @@ -37,7 +37,8 @@ spring: type: CAFFEINE # change to type redis if usage of redis distributed cache is intended # the following redis properties are only used if the cache.type=redis - redis: + data: + redis: host: localhost port: 6379 @@ -190,15 +191,10 @@ management: # Enable / disable metrics endpoint enabled: false # Prometheus metric endpoint - Special metrics format to display in microservice observer solutions - prometheus: - # Enable / disable prometheus endpoint - enabled: false - # Metrics settings - metrics: - export: - prometheus: + prometheus: + metrics: + export: enabled: true - # External Terminology Validation Properties validation: external-terminology: diff --git a/base/pom.xml b/base/pom.xml index 016c12639..0c1b27865 100644 --- a/base/pom.xml +++ b/base/pom.xml @@ -28,7 +28,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 base diff --git a/bom/pom.xml b/bom/pom.xml index 9b8ef6780..11d272194 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -24,7 +24,7 @@ org.ehrbase.openehr bom - 0.30.0 + 0.31.0 pom EHRbase @@ -103,7 +103,7 @@ 3.3.0 2.13.0 3.13.0 - 2.2.0 + 2.3.0 8.5.13 2.15.0 1.95.0 @@ -118,9 +118,9 @@ 1.6.13 3.1.2 42.5.3 - 1.11.2 - 2.7.14 - 1.6.6 + 1.11.4 + 3.1.4 + 2.2.0 2.20.0 1.4.13 @@ -180,6 +180,29 @@ pom import + + + org.ehrbase.openehr.sdk + serialisation + ${ehrbase.sdk.version} + + + commons-logging + commons-logging + + + + + org.ehrbase.openehr.sdk + validation + ${ehrbase.sdk.version} + + + commons-logging + commons-logging + + + commons-io commons-io @@ -302,7 +325,7 @@ org.springdoc - springdoc-openapi-ui + springdoc-openapi-starter-webmvc-ui ${springdoc-openapi.version} @@ -354,6 +377,12 @@ java-jwt ${java-jwt.version} + + com.sun.xml.bind + jaxb-impl + 2.3.6 + + diff --git a/jooq-pq/pom.xml b/jooq-pq/pom.xml index 148e374f8..524c9fc8c 100644 --- a/jooq-pq/pom.xml +++ b/jooq-pq/pom.xml @@ -28,7 +28,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 jooq-pg diff --git a/plugin/pom.xml b/plugin/pom.xml index 14c7c1b59..30c682ccf 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -5,7 +5,7 @@ server org.ehrbase.openehr - 0.30.0 + 0.31.0 4.0.0 @@ -16,6 +16,7 @@ + com.nedap.healthcare.archie openehr-rm diff --git a/plugin/src/main/java/org/ehrbase/plugin/security/PluginSecurityConfiguration.java b/plugin/src/main/java/org/ehrbase/plugin/security/PluginSecurityConfiguration.java index df62f628f..a30f9fbb2 100644 --- a/plugin/src/main/java/org/ehrbase/plugin/security/PluginSecurityConfiguration.java +++ b/plugin/src/main/java/org/ehrbase/plugin/security/PluginSecurityConfiguration.java @@ -17,6 +17,7 @@ */ package org.ehrbase.plugin.security; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.List; import java.util.Objects; @@ -26,11 +27,12 @@ import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.SourceLocation; import org.aspectj.runtime.internal.AroundClosure; +import org.ehrbase.api.annotations.EhrbaseSecurity; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.aspect.AnnotationAspect; import org.ehrbase.api.aspect.AuthorizationAspect; import org.ehrbase.api.aspect.TenantAspect; -import org.ehrbase.api.authorization.EhrbaseAuthorization; +import org.ehrbase.plugin.security.AuthorizationInfo.AuthorizationEnabled; import org.springframework.aop.Advisor; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.aop.support.DefaultPointcutAdvisor; @@ -124,15 +126,18 @@ public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { } @Bean - @ConditionalOnBean(value = {AuthorizationAspect.class, AuthorizationInfo.AuthorizationEnabled.class}) + @ConditionalOnBean(value = {AuthorizationAspect.class, AuthorizationEnabled.class}) public Advisor authorizationAspect() { ApplicationContext parentCtx = applicationContext.getParent(); AuthorizationAspect theAspect = parentCtx.getBean(AuthorizationAspect.class); return new DefaultPointcutAdvisor( - new AnnotationMatchingPointcut(null, EhrbaseAuthorization.class, true), new AspectAdapter(theAspect) { + new AnnotationMatchingPointcut(null, EhrbaseSecurity.class, true), new AspectAdapter(theAspect) { public Object invoke(MethodInvocation invocation) throws Throwable { - return getAspect().action(new ProceedingJoinPointAdapter(invocation), null); + Method method = invocation.getMethod(); + + Annotation[] annotations = method.getDeclaredAnnotations(); + return getAspect().action(new ProceedingJoinPointAdapter(invocation), List.of(annotations)); } }); } diff --git a/pom.xml b/pom.xml index 69cba80d1..fb84da02a 100644 --- a/pom.xml +++ b/pom.xml @@ -28,12 +28,12 @@ org.ehrbase.openehr bom - 0.30.0 + 0.31.0 /bom/pom.xml server - 0.30.0 + 0.31.0 pom @@ -172,7 +172,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.3.0 + 3.4.1 diff --git a/rest-ehr-scape/pom.xml b/rest-ehr-scape/pom.xml index 41f8864da..d84023f31 100644 --- a/rest-ehr-scape/pom.xml +++ b/rest-ehr-scape/pom.xml @@ -28,7 +28,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 rest-ehr-scape @@ -56,7 +56,7 @@ org.springdoc - springdoc-openapi-ui + springdoc-openapi-starter-webmvc-ui org.junit.vintage diff --git a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/EhrScapeExceptionHandler.java b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/EhrScapeExceptionHandler.java index 5e01dc39d..0498975a2 100644 --- a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/EhrScapeExceptionHandler.java +++ b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/EhrScapeExceptionHandler.java @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; @@ -126,7 +127,7 @@ public ResponseEntity handleUnsupportedMediaTypeException(UnsupportedMed @ExceptionHandler(ResponseStatusException.class) public ResponseEntity handleSpringResponseStatusException(ResponseStatusException ex) { // rethrow will not work properly, so we handle it - return handleExceptionInternal(ex, ex.getReason(), ex.getResponseHeaders(), ex.getStatus()); + return handleExceptionInternal(ex, ex.getReason(), ex.getResponseHeaders(), ex.getStatusCode()); } // 500 - general @@ -137,7 +138,7 @@ public ResponseEntity handleUncaughtException(Exception ex) { } private ResponseEntity handleExceptionInternal( - Exception ex, String message, HttpHeaders headers, HttpStatus status) { + Exception ex, String message, HttpHeaders headers, HttpStatusCode status) { if (status.is5xxServerError()) { logger.error("", ex); @@ -149,7 +150,9 @@ private ResponseEntity handleExceptionInternal( } Map body = new HashMap<>(); - body.put("error", status.getReasonPhrase()); + if (status instanceof HttpStatus httpStatus) { + body.put("error", httpStatus.getReasonPhrase()); + } body.put("message", message); return new ResponseEntity<>(body, headers, status); } diff --git a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/CompositionController.java b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/CompositionController.java index f5fe3c6e8..c64cbdcf7 100644 --- a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/CompositionController.java +++ b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/CompositionController.java @@ -26,8 +26,6 @@ import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.service.CompositionService; @@ -40,6 +38,7 @@ import org.ehrbase.rest.ehrscape.responsedata.CompositionWriteRestResponseData; import org.ehrbase.rest.ehrscape.responsedata.Meta; import org.ehrbase.rest.ehrscape.responsedata.RestHref; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -52,6 +51,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = {"primarycompositioncontroller"}) @TenantAware @RestController @RequestMapping( @@ -65,7 +65,6 @@ public CompositionController(CompositionService compositionService) { this.compositionService = Objects.requireNonNull(compositionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_CREATE) @PostMapping public ResponseEntity createComposition( @RequestParam(value = "format", defaultValue = "XML") CompositionFormat format, @@ -97,7 +96,6 @@ public ResponseEntity createComposition( .body(responseData); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping(path = "/{uid}") public ResponseEntity getComposition( @PathVariable("uid") String compositionUid, @@ -136,7 +134,6 @@ public ResponseEntity getComposition( } } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_UPDATE) @PutMapping(path = "/{uid}") public ResponseEntity update( @PathVariable("uid") String compositionUid, @@ -170,7 +167,6 @@ public ResponseEntity update( return ResponseEntity.ok(responseData); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_DELETE) @DeleteMapping(path = "/{uid}") public ResponseEntity delete(@PathVariable("uid") String compositionUid) { diff --git a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/EhrController.java b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/EhrController.java index b2b6f7596..6fea006bc 100644 --- a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/EhrController.java +++ b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/EhrController.java @@ -33,8 +33,6 @@ import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.GeneralRequestProcessingException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.service.EhrService; @@ -44,6 +42,7 @@ import org.ehrbase.rest.ehrscape.responsedata.EhrResponseData; import org.ehrbase.rest.ehrscape.responsedata.Meta; import org.ehrbase.rest.ehrscape.responsedata.RestHref; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -64,6 +63,7 @@ * @author Stefan Spiska * @author Jake Smolka */ +@ConditionalOnMissingBean(name = "primaryehrcontroller") @TenantAware @RestController @RequestMapping(path = API_ECIS_CONTEXT_PATH_WITH_VERSION + "/ehr") @@ -79,7 +79,6 @@ public EhrController(EhrService ehrService) { this.ehrService = Objects.requireNonNull(ehrService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_CREATE) @PostMapping @ResponseStatus(value = HttpStatus.CREATED) // overwrites default 200, fixes the wrong listing of 200 in swagger-ui (EHR-56) @@ -115,7 +114,6 @@ public ResponseEntity createEhr( .orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ) @GetMapping public ResponseEntity getEhr( @RequestParam(value = "subjectId") String subjectId, @@ -128,7 +126,6 @@ public ResponseEntity getEhr( .orElse(ResponseEntity.noContent().build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ) @GetMapping(path = "/{uuid}") public ResponseEntity getEhr( @PathVariable("uuid") UUID ehrId, @@ -140,7 +137,6 @@ public ResponseEntity getEhr( .orElse(ResponseEntity.notFound().build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_UPDATE_STATUS) @PutMapping(path = "/{uuid}/status") public ResponseEntity updateStatus( @PathVariable("uuid") UUID ehrId, diff --git a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/QueryController.java b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/QueryController.java index 45cd2da4e..8e6d1704e 100644 --- a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/QueryController.java +++ b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/QueryController.java @@ -22,13 +22,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.service.QueryService; import org.ehrbase.rest.ehrscape.responsedata.Action; import org.ehrbase.rest.ehrscape.responsedata.QueryResponseData; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -37,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = "primaryquerycontroller") @TenantAware @RestController @RequestMapping( @@ -51,7 +51,6 @@ public QueryController(QueryService queryService) { this.queryService = Objects.requireNonNull(queryService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_SEARCH_AD_HOC) @PostMapping public ResponseEntity query( @RequestParam(value = "explain", defaultValue = "false") Boolean explain, @RequestBody String content) { diff --git a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/TemplateController.java b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/TemplateController.java index 99d766d6b..1ebb72ae1 100644 --- a/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/TemplateController.java +++ b/rest-ehr-scape/src/main/java/org/ehrbase/rest/ehrscape/controller/TemplateController.java @@ -27,8 +27,6 @@ import java.util.Objects; import org.apache.xmlbeans.XmlException; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.service.CompositionService; import org.ehrbase.api.service.TemplateService; @@ -43,6 +41,7 @@ import org.ehrbase.rest.ehrscape.responsedata.TemplatesResponseData; import org.openehr.schemas.v1.TemplateDocument; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -53,6 +52,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = "primarytemplatecontroller") @TenantAware @RestController @RequestMapping( @@ -69,7 +69,6 @@ public TemplateController(TemplateService templateService, CompositionService co this.compositionService = Objects.requireNonNull(compositionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_READ) @GetMapping() public ResponseEntity getTemplate() { TemplatesResponseData responseData = new TemplatesResponseData(); @@ -78,7 +77,6 @@ public ResponseEntity getTemplate() { return ResponseEntity.ok(responseData); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_CREATE) @PostMapping() public ResponseEntity createTemplate(@RequestBody() String content) { @@ -97,7 +95,6 @@ public ResponseEntity createTemplate(@RequestBody() Strin return ResponseEntity.ok(responseData); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_EXAMPLE) @GetMapping(path = "/{templateId}/example") public ResponseEntity getTemplateExample( @PathVariable(value = "templateId") String templateId, @@ -118,7 +115,6 @@ public ResponseEntity getTemplateExample( return ResponseEntity.ok().contentType(contentType).body(serialized.getValue()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_READ) @GetMapping(path = "/{templateId}") public ResponseEntity getTemplate(@PathVariable(value = "templateId") String templateId) { TemplateResponseData responseData = new TemplateResponseData(); diff --git a/rest-openehr/pom.xml b/rest-openehr/pom.xml index 011657a6a..4999be507 100644 --- a/rest-openehr/pom.xml +++ b/rest-openehr/pom.xml @@ -28,7 +28,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 rest-openehr @@ -38,6 +38,7 @@ + org.springframework.boot spring-boot-starter-web @@ -60,7 +61,7 @@ org.springdoc - springdoc-openapi-ui + springdoc-openapi-starter-webmvc-ui com.auth0 diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/DefaultExceptionHandler.java b/rest-openehr/src/main/java/org/ehrbase/rest/DefaultExceptionHandler.java index be3f86b7e..498d82913 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/DefaultExceptionHandler.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/DefaultExceptionHandler.java @@ -34,9 +34,9 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.security.access.AccessDeniedException; import org.springframework.validation.BindException; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; @@ -79,11 +79,6 @@ public ResponseEntity handleBadRequestExceptions(Exception ex) { return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST); } - @ExceptionHandler(AccessDeniedException.class) - public ResponseEntity handleObjectNotFoundException(AccessDeniedException ex) { - return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.FORBIDDEN); - } - // 404 @ExceptionHandler(ObjectNotFoundException.class) public ResponseEntity handleObjectNotFoundException(ObjectNotFoundException ex) { @@ -133,7 +128,7 @@ public ResponseEntity handleUnprocessableEntityException( @ExceptionHandler(ResponseStatusException.class) public ResponseEntity handleSpringResponseStatusException(ResponseStatusException ex) { // rethrow will not work properly, so we handle it - return handleExceptionInternal(ex, ex.getReason(), ex.getResponseHeaders(), ex.getStatus()); + return handleExceptionInternal(ex, ex.getReason(), ex.getResponseHeaders(), ex.getStatusCode()); } // 500 - general @@ -144,7 +139,7 @@ public ResponseEntity handleUncaughtException(Exception ex) { } private ResponseEntity handleExceptionInternal( - Exception ex, String message, HttpHeaders headers, HttpStatus status) { + Exception ex, String message, HttpHeaders headers, HttpStatusCode status) { if (status.is5xxServerError()) { logger.error("", ex); @@ -156,7 +151,9 @@ private ResponseEntity handleExceptionInternal( } Map body = new HashMap<>(); - body.put("error", status.getReasonPhrase()); + if (status instanceof HttpStatus httpStatus) { + body.put("error", httpStatus.getReasonPhrase()); + } body.put("message", message); return new ResponseEntity<>(body, headers, status); } diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/RestModuleConfiguration.java b/rest-openehr/src/main/java/org/ehrbase/rest/RestModuleConfiguration.java index 7cb1fd761..36e7bab44 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/RestModuleConfiguration.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/RestModuleConfiguration.java @@ -17,9 +17,9 @@ */ package org.ehrbase.rest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.ehrbase.api.tenant.TenantAuthentication; import org.ehrbase.api.tenant.ThreadLocalSupplier; import org.springframework.beans.factory.annotation.Value; diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/StatusController.java b/rest-openehr/src/main/java/org/ehrbase/rest/StatusController.java index f8bcfbc5c..05d80cbf9 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/StatusController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/StatusController.java @@ -26,11 +26,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.Objects; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.service.StatusService; import org.ehrbase.openehr.sdk.response.dto.StatusResponseData; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -45,6 +44,7 @@ * API endpoint to get status of EHRbase and version information on used dependencies as archie or openEHR_sdk as well * as the current used JVM version or target PostgreSQL server version. */ +@ConditionalOnMissingBean(name = "primarystatuscontroller") @Tag(name = "Status", description = "Heartbeat, Version info, Status") @RestController @RequestMapping( @@ -59,7 +59,6 @@ public StatusController(StatusService statusService) { this.statusService = Objects.requireNonNull(statusService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_SYSTEM_STATUS) @GetMapping(path = "/status") @Operation(summary = "Get status information on running EHRbase server instance") @ApiResponses( diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminCompositionController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminCompositionController.java index 46001e33d..7d881c465 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminCompositionController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminCompositionController.java @@ -27,14 +27,13 @@ import java.util.UUID; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.service.CompositionService; import org.ehrbase.api.service.EhrService; import org.ehrbase.openehr.sdk.response.dto.admin.AdminDeleteResponseData; import org.ehrbase.rest.BaseController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -47,9 +46,10 @@ /** * Admin API controller for Composition related data. Provides endpoint to remove compositions physically from database. */ +@ConditionalOnMissingBean(name = "primaryadmincompositioncontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - Composition") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = BaseController.ADMIN_API_CONTEXT_PATH + "/ehr", @@ -65,8 +65,6 @@ public AdminCompositionController(EhrService ehrService, CompositionService comp this.compositionService = Objects.requireNonNull(compositionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_DELETE) @DeleteMapping(path = "/{ehr_id}/composition/{composition_id}") @ApiResponses( value = { @@ -110,7 +108,7 @@ public ResponseEntity deleteComposition( .setEhrIds(ehrId) .setCompositionId(compositionId) .setTemplateId(compositionService.retrieveTemplateId(compositionUid)) - .setLocation(UriComponentsBuilder.fromPath("/{ehr_id}/composition/{composition_id}") + .setLocation(UriComponentsBuilder.fromPath("/ehr/{ehr_id}/composition/{composition_id}") .build(ehrId, compositionId) .toString()); diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminContributionController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminContributionController.java index 474692931..6ac141752 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminContributionController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminContributionController.java @@ -29,8 +29,6 @@ import java.util.UUID; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.service.ContributionService; import org.ehrbase.api.service.EhrService; @@ -38,6 +36,7 @@ import org.ehrbase.openehr.sdk.response.dto.admin.AdminUpdateResponseData; import org.ehrbase.rest.BaseController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -51,9 +50,10 @@ * Admin API controller for Contribution related data. Provides endpoints to update and remove Contributions in * database physically. */ +@ConditionalOnMissingBean(name = "primaryadmincontributioncontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - Contribution") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = "${admin-api.context-path:/rest/admin}/ehr", @@ -69,8 +69,6 @@ public AdminContributionController(EhrService ehrService, ContributionService co this.contributionService = contributionService; } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_CONTRIBUTION_UPDATE) @PutMapping( path = "/{ehr_id}/contribution/{contribution_id}", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @@ -116,8 +114,6 @@ public ResponseEntity updateContribution( return ResponseEntity.ok().body(new AdminUpdateResponseData(0)); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_CONTRIBUTION_DELETE) @DeleteMapping(path = "/{ehr_id}/contribution/{contribution_id}") @ApiResponses( value = { diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminController.java index 48f6ddc61..fe803c854 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminController.java @@ -23,10 +23,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.openehr.sdk.response.dto.admin.AdminStatusResponseData; import org.ehrbase.rest.BaseController; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -34,17 +33,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = "primaryadmincontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - Heartbeat") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = "${admin-api.context-path:/rest/admin}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) public class AdminController extends BaseController { - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_SYSTEM_STATUS) @GetMapping(path = "/status") @ApiResponses( value = { diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminDirectoryController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminDirectoryController.java index 3bd4f1509..b129fe890 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminDirectoryController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminDirectoryController.java @@ -28,12 +28,11 @@ import java.util.UUID; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.service.DirectoryService; import org.ehrbase.openehr.sdk.response.dto.admin.AdminDeleteResponseData; import org.ehrbase.rest.BaseController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -45,9 +44,10 @@ /** * Admin API controller for directories. Provides endpoint to remove complete directory trees from database physically. */ +@ConditionalOnMissingBean(name = "primaryadmindirectorycontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - Directory") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = "${admin-api.context-path:/rest/admin}/ehr", @@ -62,8 +62,6 @@ public AdminDirectoryController(DirectoryService directoryService) { this.directoryService = directoryService; } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_DELETE) @DeleteMapping(path = "/{ehr_id}/directory/{directory_id}") @ApiResponses( value = { diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminEhrController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminEhrController.java index fe17c03cc..b23772fd8 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminEhrController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminEhrController.java @@ -29,14 +29,13 @@ import java.util.stream.Collectors; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.service.EhrService; import org.ehrbase.openehr.sdk.response.dto.admin.AdminDeleteResponseData; import org.ehrbase.openehr.sdk.response.dto.admin.AdminUpdateResponseData; import org.ehrbase.rest.BaseController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -51,9 +50,10 @@ /** * Admin API controller for EHR related endpoints. Provides methods to update and delete EHRs physically in the DB. */ +@ConditionalOnMissingBean(name = "primaryadminehrcontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - EHR") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = BaseController.ADMIN_API_CONTEXT_PATH + "/ehr", @@ -67,8 +67,6 @@ public AdminEhrController(EhrService ehrService) { this.ehrService = ehrService; } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_UPDATE) @PutMapping( path = "/{ehr_id}", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @@ -112,8 +110,6 @@ public ResponseEntity updateEhr( return ResponseEntity.ok().body(new AdminUpdateResponseData(0)); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_DELETE) @DeleteMapping(path = "/{ehr_id}") @ApiResponses( value = { diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminTemplateController.java b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminTemplateController.java index 9c7c75a7c..0128952db 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminTemplateController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/admin/AdminTemplateController.java @@ -24,13 +24,12 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.service.TemplateService; import org.ehrbase.openehr.sdk.response.dto.admin.AdminDeleteResponseData; import org.ehrbase.openehr.sdk.response.dto.admin.AdminStatusResponseData; import org.ehrbase.rest.BaseController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -47,9 +46,10 @@ /** * Admin API controller for Templates. Provides endpoints to update (replace) and delete templates. */ +@ConditionalOnMissingBean(name = "primaryadmintemplatecontroller") +@ConditionalOnProperty(prefix = "admin-api", name = "active") @TenantAware @Tag(name = "Admin - Template") -@ConditionalOnProperty(prefix = "admin-api", name = "active") @RestController @RequestMapping( path = "${admin-api.context-path:/rest/admin}/template", @@ -59,15 +59,13 @@ public class AdminTemplateController extends BaseController { TemplateService templateService; @Autowired - AdminTemplateController(TemplateService templateService) { + public AdminTemplateController(TemplateService templateService) { this.templateService = templateService; } @Autowired AdminApiConfiguration adminApiConfiguration; - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_UPDATE) @PutMapping( path = "/{template_id}", consumes = {MediaType.APPLICATION_XML_VALUE}, @@ -111,8 +109,6 @@ public ResponseEntity updateTemplate( return ResponseEntity.ok().headers(headers).body(updatedTemplate); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_DELETE) @DeleteMapping(path = "/{template_id}") @ApiResponses( value = { @@ -136,8 +132,6 @@ public ResponseEntity deleteTemplate( return ResponseEntity.ok().body(new AdminDeleteResponseData(deleted)); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_ADMIN_ACCESS) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_DELETE) @DeleteMapping(path = "/all") @ApiResponses( value = { diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrCompositionController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrCompositionController.java index 16eccf126..02b29d8e6 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrCompositionController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrCompositionController.java @@ -36,8 +36,6 @@ import java.util.function.Supplier; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.exception.PreconditionFailedException; @@ -51,6 +49,7 @@ import org.ehrbase.rest.openehr.specification.CompositionApiSpecification; import org.ehrbase.rest.util.InternalResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -76,6 +75,7 @@ * @author Jake Smolka * @since 1.0.0 */ +@ConditionalOnMissingBean(name = "primaryopenehrcompositioncontroller") @TenantAware @RestController @RequestMapping( @@ -90,7 +90,6 @@ public OpenehrCompositionController(CompositionService compositionService) { this.compositionService = Objects.requireNonNull(compositionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_CREATE) @PostMapping( value = "/{ehr_id}/composition", consumes = {"application/xml", "application/json"}) @@ -156,7 +155,6 @@ public ResponseEntity createComposition( .orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_UPDATE) @PutMapping("/{ehr_id}/composition/{versioned_object_uid}") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PreAuthorize("checkAbacPre(@openehrCompositionController.COMPOSITION, " @@ -252,7 +250,6 @@ public ResponseEntity updateComposition( .orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_DELETE) @DeleteMapping("/{ehr_id}/composition/{preceding_version_uid}") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PreAuthorize("checkAbacPre(@openehrCompositionController.COMPOSITION, " @@ -331,7 +328,6 @@ public ResponseEntity deleteComposition( * because of the overlapping paths. Both mappings are specified to behave almost the same, so * this solution works in this case. */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping("/{ehr_id}/composition/{versioned_object_uid}") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PostAuthorize("checkAbacPost(@openehrCompositionController.COMPOSITION, " @@ -404,7 +400,7 @@ private String getLocationUrl(UUID versionedObjectUid, UUID ehrId, int version) version = compositionService.getLastVersionNumber(versionedObjectUid); } - return fromPath("{ehrSegment}/{ehrId}/{compositionSegment}/{compositionId}::{nodeName}::{version}") + return fromPath("/{ehrSegment}/{ehrId}/{compositionSegment}/{compositionId}::{nodeName}::{version}") .build( EHR, ehrId.toString(), diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrContributionController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrContributionController.java index 51f40c252..5f8912b6c 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrContributionController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrContributionController.java @@ -33,8 +33,6 @@ import java.util.function.Supplier; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.NotAcceptableException; import org.ehrbase.api.service.ContributionService; import org.ehrbase.openehr.sdk.response.dto.ContributionResponseData; @@ -44,6 +42,7 @@ import org.ehrbase.rest.openehr.specification.ContributionApiSpecification; import org.ehrbase.rest.util.InternalResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -58,6 +57,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = "primaryopenehrcontributioncontroller") @TenantAware @RestController @RequestMapping( @@ -72,7 +72,6 @@ public OpenehrContributionController(ContributionService contributionService) { this.contributionService = Objects.requireNonNull(contributionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_CONTRIBUTION_CREATE) @PostMapping( value = "/{ehr_id}/contribution", consumes = {"application/xml", "application/json"}) @@ -133,7 +132,6 @@ public ResponseEntity createContribution( .orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_CONTRIBUTION_READ) @GetMapping(value = "/{ehr_id}/contribution/{contribution_uid}") @Override public ResponseEntity getContribution( diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDefinitionQueryController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDefinitionQueryController.java index 25704fe50..0e8f8f3ce 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDefinitionQueryController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDefinitionQueryController.java @@ -18,7 +18,11 @@ package org.ehrbase.rest.openehr; import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.springframework.http.MediaType.*; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.APPLICATION_XML_VALUE; +import static org.springframework.http.MediaType.TEXT_PLAIN; +import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; import static org.springframework.web.util.UriComponentsBuilder.fromPath; import com.fasterxml.jackson.core.JsonProcessingException; @@ -28,8 +32,6 @@ import java.util.Optional; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.GeneralRequestProcessingException; import org.ehrbase.api.exception.UnexpectedSwitchCaseException; import org.ehrbase.api.exception.UnsupportedMediaTypeException; @@ -43,6 +45,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -58,6 +61,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@ConditionalOnMissingBean(name = "primaryopenehrdefinitionquerycontroller") @TenantAware @RestController @RequestMapping( @@ -88,7 +92,6 @@ public OpenehrDefinitionQueryController(QueryService queryService) { */ @Override @GetMapping(value = {"/{qualified_query_name}", ""}) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_READ) public ResponseEntity getStoredQueryList( @RequestHeader(value = ACCEPT, required = false) String accept, @PathVariable(value = "qualified_query_name", required = false) String qualifiedQueryName) { @@ -105,7 +108,6 @@ public ResponseEntity getStoredQueryList( @Override @GetMapping(value = {"/{qualified_query_name}/{version}"}) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_READ) public ResponseEntity getStoredQueryVersion( @RequestHeader(value = ACCEPT, required = false) String accept, @PathVariable(value = "qualified_query_name") String qualifiedQueryName, @@ -124,7 +126,6 @@ public ResponseEntity getStoredQueryVersion( } @Override - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_CREATE) @PutMapping( value = {"/{qualified_query_name}/{version}", "/{qualified_query_name}"}, consumes = {TEXT_PLAIN_VALUE, APPLICATION_JSON_VALUE}, @@ -190,7 +191,6 @@ public ResponseEntity putStoredQuery( @Override @DeleteMapping(value = {"/{qualified_query_name}/{version}"}) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_DELETE) public ResponseEntity deleteStoredQuery( @RequestHeader(value = ACCEPT, required = false) String accept, @PathVariable(value = "qualified_query_name") String qualifiedQueryName, diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDirectoryController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDirectoryController.java index f74aa9995..6e2b59912 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDirectoryController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrDirectoryController.java @@ -18,6 +18,8 @@ package org.ehrbase.rest.openehr; import static org.apache.commons.lang3.StringUtils.unwrap; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.POST; import static org.springframework.web.util.UriComponentsBuilder.fromPath; import com.nedap.archie.rm.directory.Folder; @@ -30,8 +32,6 @@ import org.apache.commons.lang3.StringUtils; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.service.DirectoryService; @@ -39,6 +39,7 @@ import org.ehrbase.rest.BaseController; import org.ehrbase.rest.openehr.specification.DirectoryApiSpecification; import org.joda.time.DateTime; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -63,6 +64,7 @@ * @author Renaud Subiger * @since 1.0 */ +@ConditionalOnMissingBean(name = "primaryopenehrdirectorycontroller") @TenantAware @RestController @RequestMapping(path = BaseController.API_CONTEXT_PATH_WITH_VERSION + "/ehr") @@ -77,7 +79,6 @@ public OpenehrDirectoryController(DirectoryService directoryService) { /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_CREATE) @Override @PostMapping(path = "/{ehr_id}/directory") public ResponseEntity createDirectory( @@ -91,13 +92,12 @@ public ResponseEntity createDirectory( var createdFolder = directoryService.create(ehrId, folder); - return createDirectoryResponse(HttpMethod.POST, prefer, accept, createdFolder, ehrId); + return createDirectoryResponse(POST, prefer, accept, createdFolder, ehrId); } /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_UPDATE) @Override @PutMapping(path = "/{ehr_id}/directory") public ResponseEntity updateDirectory( @@ -121,7 +121,6 @@ public ResponseEntity updateDirectory( /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_DELETE) @Override @DeleteMapping(path = "/{ehr_id}/directory") public ResponseEntity deleteDirectory( @@ -136,13 +135,12 @@ public ResponseEntity deleteDirectory( directoryService.delete(ehrId, folderId); createAuditLogsMsgBuilder(ehrId.toString(), folderId.toString()); - return createDirectoryResponse(HttpMethod.DELETE, null, accept, null, ehrId); + return createDirectoryResponse(DELETE, null, accept, null, ehrId); } /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_READ) @Override @GetMapping(path = "/{ehr_id}/directory/{version_uid}") public ResponseEntity getFolderInDirectory( @@ -183,7 +181,6 @@ private void validateVersionUid(ObjectVersionId versionUid) { } } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_DIRECTORY_READ) @Override @GetMapping(path = "/{ehr_id}/directory") public ResponseEntity getFolderInDirectoryVersionAtTime( @@ -256,17 +253,12 @@ private ResponseEntity createDirectoryResponse( } private HttpStatus getSuccessStatus(HttpMethod method) { - switch (method) { - case POST: { - return HttpStatus.CREATED; - } - case DELETE: { - return HttpStatus.NO_CONTENT; - } - default: { - return HttpStatus.OK; - } + if (method.equals(POST)) { + return HttpStatus.CREATED; + } else if (method.equals(DELETE)) { + return HttpStatus.NO_CONTENT; } + return HttpStatus.OK; } /** diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrController.java index 515b968fc..86ed44d8a 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrController.java @@ -29,8 +29,6 @@ import java.util.function.Supplier; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; @@ -41,6 +39,7 @@ import org.ehrbase.rest.openehr.specification.EhrApiSpecification; import org.ehrbase.rest.util.InternalResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -60,6 +59,7 @@ /** * Controller for /ehr resource of openEHR REST API */ +@ConditionalOnMissingBean(name = "primaryopenehrehrcontroller") @TenantAware @RestController @RequestMapping( @@ -74,11 +74,9 @@ public OpenehrEhrController(EhrService ehrService) { this.ehrService = Objects.requireNonNull(ehrService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_CREATE) @PostMapping // (consumes = {"application/xml", "application/json"}) @ResponseStatus(value = HttpStatus.CREATED) // TODO auditing headers (openehr*) ignored until auditing is implemented - @Override public ResponseEntity createEhr( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) String openehrAuditDetails, @@ -97,10 +95,8 @@ public ResponseEntity createEhr( return internalPostEhrProcessing(accept, prefer, ehrId); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_CREATE) @PutMapping(path = "/{ehr_id}") @ResponseStatus(value = HttpStatus.CREATED) - @Override public ResponseEntity createEhrWithId( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) String openehrAuditDetails, @@ -172,10 +168,8 @@ private void createAuditLogsMsgBuilder(UUID resultEhrId) { /** * Returns EHR by ID */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ) @GetMapping(path = "/{ehr_id}") @PreAuthorize("checkAbacPre(@openehrEhrController.EHR, @ehrService.getSubjectExtRef(#ehrIdString))") - @Override public ResponseEntity retrieveEhrById( @RequestHeader(value = HttpHeaders.ACCEPT, required = false) String accept, @PathVariable(value = "ehr_id") String ehrIdString) { @@ -192,10 +186,8 @@ public ResponseEntity retrieveEhrById( /** * Returns EHR by subject (id and namespace) */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ) @GetMapping(params = {"subject_id", "subject_namespace"}) @PreAuthorize("checkAbacPre(@openehrEhrController.EHR, #subjectId)") - @Override public ResponseEntity retrieveEhrBySubject( @RequestHeader(value = HttpHeaders.ACCEPT, required = false) String accept, @RequestParam(value = "subject_id") String subjectId, diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrStatusController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrStatusController.java index ca71b2978..9a08ce3a4 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrStatusController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrEhrStatusController.java @@ -31,8 +31,6 @@ import java.util.function.Supplier; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; @@ -42,6 +40,7 @@ import org.ehrbase.rest.BaseController; import org.ehrbase.rest.openehr.specification.EhrStatusApiSpecification; import org.ehrbase.rest.util.InternalResponse; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -63,6 +62,7 @@ * @author Renaud Subiger * @since 1.0 */ +@ConditionalOnMissingBean(name = "primaryopenehrehrstatuscontroller") @TenantAware @RestController @RequestMapping(path = BaseController.API_CONTEXT_PATH_WITH_VERSION + "/ehr/{ehr_id}/ehr_status") @@ -79,7 +79,6 @@ public OpenehrEhrStatusController(EhrService ehrService) { */ @Override @GetMapping - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @PreAuthorize("checkAbacPre(@openehrEhrStatusController.EHR_STATUS, @ehrService.getSubjectExtRef(#ehrId))") public ResponseEntity getEhrStatusVersionByTime( @PathVariable(name = "ehr_id") UUID ehrId, @@ -109,7 +108,6 @@ public ResponseEntity getEhrStatusVersionByTime( */ @Override @GetMapping(path = "/{version_uid}") - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @PreAuthorize("checkAbacPre(@openehrEhrStatusController.EHR_STATUS, @ehrService.getSubjectExtRef(#ehrId))") public ResponseEntity getEhrStatusByVersionId( @PathVariable(name = "ehr_id") UUID ehrId, @@ -137,7 +135,6 @@ public ResponseEntity getEhrStatusByVersionId( */ @Override @PutMapping - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_UPDATE_STATUS) @PreAuthorize("checkAbacPre(@openehrEhrStatusController.EHR_STATUS, @ehrService.getSubjectExtRef(#ehrId))") public ResponseEntity updateEhrStatus( @PathVariable("ehr_id") UUID ehrId, diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrQueryController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrQueryController.java index 1a11e1b9d..004c72792 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrQueryController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrQueryController.java @@ -27,8 +27,6 @@ import org.apache.commons.collections4.MapUtils; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; import org.ehrbase.api.service.QueryService; @@ -41,6 +39,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; import org.springframework.security.access.prepost.PostAuthorize; @@ -61,6 +60,7 @@ * @author Renaud Subiger * @since 1.0 */ +@ConditionalOnMissingBean(name = "primaryopenehrquerycontroller") @TenantAware @RestController @RequestMapping(path = BaseController.API_CONTEXT_PATH_WITH_VERSION + "/query") @@ -84,8 +84,6 @@ public OpenehrQueryController(QueryService queryService) { /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_SEARCH_AD_HOC) - @Override @GetMapping(path = "/aql") @PostAuthorize("checkAbacPostQuery(@requestAwareAuditResultMapHolder.getAuditResultMap())") public ResponseEntity executeAdHocQuery( @@ -119,8 +117,6 @@ public ResponseEntity executeAdHocQuery( /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_SEARCH_AD_HOC) - @Override @PostMapping(path = "/aql") @PostAuthorize("checkAbacPostQuery(@requestAwareAuditResultMapHolder.getAuditResultMap())") @SuppressWarnings("unchecked") @@ -149,8 +145,6 @@ public ResponseEntity executeAdHocQuery( /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_SEARCH) - @Override @GetMapping(path = {"/{qualified_query_name}", "/{qualified_query_name}/{version}"}) @PostAuthorize("checkAbacPostQuery(@requestAwareAuditResultMapHolder.getAuditResultMap())") public ResponseEntity executeStoredQuery( @@ -197,8 +191,6 @@ public ResponseEntity executeStoredQuery( /** * {@inheritDoc} */ - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_QUERY_SEARCH) - @Override @PostMapping(path = {"/{qualified_query_name}", "/{qualified_query_name}/{version}"}) @PostAuthorize("checkAbacPostQuery(@requestAwareAuditResultMapHolder.getAuditResultMap())") @SuppressWarnings("unchecked") diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrTemplateController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrTemplateController.java index 745f6cdc4..a2ebcd87c 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrTemplateController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrTemplateController.java @@ -33,8 +33,6 @@ import java.util.function.Supplier; import org.apache.xmlbeans.XmlException; import org.ehrbase.api.annotations.TenantAware; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.definitions.OperationalTemplateFormat; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; @@ -52,6 +50,7 @@ import org.ehrbase.rest.util.InternalResponse; import org.openehr.schemas.v1.TemplateDocument; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -69,6 +68,7 @@ /** * Controller for /template resource as part of the Definitions sub-API of the openEHR REST API */ +@ConditionalOnMissingBean(name = "primaryopenehrtemplatecontroller") @TenantAware @RestController @RequestMapping( @@ -89,12 +89,10 @@ public OpenehrTemplateController(TemplateService templateService, CompositionSer /* ADL 1.4 */ - @Override @PostMapping( path = "/adl1.4", produces = {MediaType.APPLICATION_XML_VALUE}) @ResponseStatus(value = HttpStatus.CREATED) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_CREATE) public ResponseEntity createTemplateClassic( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, // TODO, see EHR-267 @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) @@ -154,9 +152,7 @@ public ResponseEntity createTemplateClassic( // Note: based on latest-branch of 1.1.0 release of openEHR REST API, because this endpoint was changed // significantly - @Override @GetMapping("/adl1.4") - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_READ) public ResponseEntity getTemplatesClassic( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, // TODO, see EHR-267 @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) @@ -182,9 +178,7 @@ public ResponseEntity getTemplatesClassic( // Note: based on latest-branch of 1.1.0 release of openEHR REST API, because this endpoint was changed // significantly - @Override @GetMapping("/adl1.4/{template_id}") - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_READ) public ResponseEntity getTemplateClassic( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, // TODO, see EHR-267 @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) @@ -210,7 +204,6 @@ public ResponseEntity getTemplateClassic( } @GetMapping(path = "/adl1.4/{template_id}/example") - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_EXAMPLE) public ResponseEntity getTemplateExample( @RequestHeader(value = ACCEPT, required = false) String accept, @PathVariable(value = "template_id") String templateId) { @@ -236,10 +229,8 @@ public ResponseEntity getTemplateExample( ADL 2 TODO WIP state only implements endpoints from outer server side, everything else is a stub. Also with a lot of duplication at the moment, which should be reduced when implementing functionality. */ - @Override @PostMapping("/adl2") @ResponseStatus(value = HttpStatus.CREATED) - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_CREATE) public ResponseEntity createTemplateNew( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, // TODO, see EHR-267 @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) @@ -273,9 +264,7 @@ public ResponseEntity createTemplateNew( // significantly // also, this endpoint combines what is listed as two endpoints: // https://specifications.openehr.org/releases/ITS-REST/latest/definitions.html#definitions-adl-2-template-get - @Override @GetMapping("/adl2/{template_id}/{version_pattern}") - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_TEMPLATE_READ) public ResponseEntity getTemplateNew( @RequestHeader(value = "openEHR-VERSION", required = false) String openehrVersion, // TODO, see EHR-267 @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedCompositionController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedCompositionController.java index 9a58e4543..290006e7c 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedCompositionController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedCompositionController.java @@ -32,8 +32,6 @@ import java.util.UUID; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; @@ -47,6 +45,7 @@ import org.ehrbase.rest.BaseController; import org.ehrbase.rest.openehr.specification.VersionedCompositionApiSpecification; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -63,6 +62,7 @@ /** * Controller for /ehr/{ehrId}/versioned_composition resource of openEHR REST API */ +@ConditionalOnMissingBean(name = "primaryopenehrversionedcompositioncontroller") @TenantAware @RestController @RequestMapping( @@ -83,7 +83,6 @@ public OpenehrVersionedCompositionController( this.contributionService = Objects.requireNonNull(contributionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping(path = "/{versioned_object_uid}") @Override public ResponseEntity> retrieveVersionedCompositionByVersionedObjectUid( @@ -111,7 +110,6 @@ public ResponseEntity> retrieveVersione return ResponseEntity.ok().headers(respHeaders).body(response); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping(path = "/{versioned_object_uid}/revision_history") @Override public ResponseEntity retrieveVersionedCompositionRevisionHistoryByEhr( @@ -139,7 +137,6 @@ public ResponseEntity retrieveVersionedCompositionR return ResponseEntity.ok().headers(respHeaders).body(response); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping(path = "/{versioned_object_uid}/version/{version_uid}") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PostAuthorize("checkAbacPost(@openehrVersionedCompositionController.COMPOSITION, " @@ -180,7 +177,6 @@ public ResponseEntity> retrieveVersionO return getOriginalVersionResponseDataResponseEntity(accept, ehrId, versionedObjectId, version); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_COMPOSITION_READ) @GetMapping(path = "/{versioned_object_uid}/version") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PostAuthorize("checkAbacPost(@openehrVersionedCompositionController.COMPOSITION, " diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedEhrStatusController.java b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedEhrStatusController.java index 8b80fee02..e3741f6b8 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedEhrStatusController.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/openehr/OpenehrVersionedEhrStatusController.java @@ -28,8 +28,6 @@ import java.util.UUID; import org.ehrbase.api.annotations.TenantAware; import org.ehrbase.api.audit.msg.AuditMsgBuilder; -import org.ehrbase.api.authorization.EhrbaseAuthorization; -import org.ehrbase.api.authorization.EhrbasePermission; import org.ehrbase.api.exception.InternalServerException; import org.ehrbase.api.exception.InvalidApiParameterException; import org.ehrbase.api.exception.ObjectNotFoundException; @@ -41,6 +39,7 @@ import org.ehrbase.openehr.sdk.response.dto.ehrscape.ContributionDto; import org.ehrbase.rest.BaseController; import org.ehrbase.rest.openehr.specification.VersionedEhrStatusApiSpecification; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -56,6 +55,7 @@ /** * Controller for /ehr/{ehrId}/versioned_ehr_status resource of openEHR REST API */ +@ConditionalOnMissingBean(name = "primaryopenehrversionedehrstatuscontroller") @TenantAware @RestController @RequestMapping( @@ -72,7 +72,6 @@ public OpenehrVersionedEhrStatusController(EhrService ehrService, ContributionSe this.contributionService = Objects.requireNonNull(contributionService); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @GetMapping @Override public ResponseEntity> retrieveVersionedEhrStatusByEhr( @@ -98,7 +97,6 @@ public ResponseEntity> retrieveVersionedE return ResponseEntity.ok().headers(respHeaders).body(response); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @GetMapping(path = "/revision_history") @Override public ResponseEntity retrieveVersionedEhrStatusRevisionHistoryByEhr( @@ -124,7 +122,6 @@ public ResponseEntity retrieveVersionedEhrStatusRev return ResponseEntity.ok().headers(respHeaders).body(response); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @GetMapping(path = "/version") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PreAuthorize("checkAbacPre(@openehrVersionedEhrStatusController.EHR_STATUS, " @@ -175,7 +172,6 @@ public ResponseEntity> retrieveVersionOfE return ResponseEntity.ok().headers(respHeaders).body(originalVersionResponseData); } - @EhrbaseAuthorization(permission = EhrbasePermission.EHRBASE_EHR_READ_STATUS) @GetMapping(path = "/version/{version_uid}") // checkAbacPre /-Post attributes (type, subject, payload, content type) @PreAuthorize("checkAbacPre(@openehrVersionedEhrStatusController.EHR_STATUS, " diff --git a/rest-openehr/src/main/java/org/ehrbase/rest/util/AuthHelper.java b/rest-openehr/src/main/java/org/ehrbase/rest/util/AuthHelper.java index c9160aafe..f5d3e7880 100644 --- a/rest-openehr/src/main/java/org/ehrbase/rest/util/AuthHelper.java +++ b/rest-openehr/src/main/java/org/ehrbase/rest/util/AuthHelper.java @@ -22,12 +22,12 @@ import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; +import jakarta.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.Base64; import java.util.Map; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.oauth2.jwt.Jwt; diff --git a/service/pom.xml b/service/pom.xml index b2945556e..1d2c9c8b6 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -26,7 +26,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 service diff --git a/service/src/main/antlr4/org/ehrbase/aql/parser/Aql.g4 b/service/src/main/antlr4/org/ehrbase/aql/parser/Aql.g4 index 461c0d0c4..c9162a589 100644 --- a/service/src/main/antlr4/org/ehrbase/aql/parser/Aql.g4 +++ b/service/src/main/antlr4/org/ehrbase/aql/parser/Aql.g4 @@ -30,10 +30,10 @@ topExpr // | TOP INTEGER FORWARD ; function - : FUNCTION_IDENTIFIER OPEN_PAR (IDENTIFIER|identifiedPath|operand|) (COMMA (IDENTIFIER|identifiedPath|operand))* CLOSE_PAR; + : FUNCTION_IDENTIFIER OPEN_PAR (function|identifiedPath|operand|) (COMMA (function|identifiedPath|operand))* CLOSE_PAR; castFunction : - CAST_FUNCTION_IDENTIFIER OPEN_PAR (IDENTIFIER|identifiedPath|operand) AS STRING CLOSE_PAR; + CAST_FUNCTION_IDENTIFIER OPEN_PAR (function|identifiedPath|operand) AS STRING CLOSE_PAR; extension : EXTENSION_IDENTIFIER OPEN_PAR STRING COMMA STRING CLOSE_PAR; diff --git a/service/src/main/java/org/ehrbase/aql/compiler/QueryCompilerPass2.java b/service/src/main/java/org/ehrbase/aql/compiler/QueryCompilerPass2.java index 416987a16..933208b9f 100644 --- a/service/src/main/java/org/ehrbase/aql/compiler/QueryCompilerPass2.java +++ b/service/src/main/java/org/ehrbase/aql/compiler/QueryCompilerPass2.java @@ -29,6 +29,7 @@ import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.commons.lang3.StringUtils; +import org.ehrbase.aql.definition.CastFunctionDefinition; import org.ehrbase.aql.definition.ConstantDefinition; import org.ehrbase.aql.definition.ExtensionDefinition; import org.ehrbase.aql.definition.FuncParameter; @@ -102,6 +103,7 @@ public class QueryCompilerPass2 extends AqlBaseListener { private final Deque variableStack = new ArrayDeque<>(); private final Map predicateDefinitionMap = new HashMap<>(); private final Random random = new Random(); + private int serial = 0; private Deque orderAttributes = null; private Integer limitAttribute = null; @@ -196,6 +198,12 @@ private void pushVariableDefinition(I_VariableDefinition variableDefinition) { private void handleFunctionDefinition( AqlParser.FunctionContext functionContext, AqlParser.SelectExprContext inSelectExprContext) { + FunctionDefinition definition = handleFunctionDefinitionInner(functionContext, inSelectExprContext); + pushVariableDefinition(definition); + } + + private FunctionDefinition handleFunctionDefinitionInner( + AqlParser.FunctionContext functionContext, AqlParser.SelectExprContext inSelectExprContext) { logger.debug("Found function"); String name = functionContext.FUNCTION_IDENTIFIER().getText(); @@ -205,8 +213,6 @@ private void handleFunctionDefinition( List parameters = new ArrayList<>(); - int serial = 0; - for (ParseTree pathTree : functionContext.children) { if (pathTree instanceof AqlParser.IdentifiedPathContext) { AqlParser.IdentifiedPathContext pathContext = (AqlParser.IdentifiedPathContext) pathTree; @@ -224,8 +230,14 @@ private void handleFunctionDefinition( variableDefinition.getAlias() == null ? variableDefinition.getPath() : variableDefinition.getAlias())); - } else if (pathTree instanceof AqlParser.OperandContext) { - parameters.add(new FuncParameter(FuncParameterType.OPERAND, pathTree.getText())); + } else if (pathTree instanceof AqlParser.OperandContext operandContext) { + + parameters.add(new FuncParameter(FuncParameterType.OPERAND, handleOperator(operandContext))); + } else if (pathTree instanceof AqlParser.FunctionContext functionContextInner) { + parameters.add(new FuncParameter( + FuncParameterType.FUNCTION, + handleFunctionDefinitionInner(functionContextInner, inSelectExprContext))); + } else if (pathTree instanceof TerminalNode) { parameters.add(new FuncParameter(FuncParameterType.IDENTIFIER, pathTree.getText())); } @@ -239,8 +251,7 @@ private void handleFunctionDefinition( } } String path = functionContext.getText(); - FunctionDefinition definition = new FunctionDefinition(name, alias, path, parameters); - pushVariableDefinition(definition); + return new FunctionDefinition(name, alias, path, parameters); } private void handleCastFunctionDefinition( @@ -249,7 +260,7 @@ private void handleCastFunctionDefinition( List parameters = new ArrayList<>(); - int serial = 0; + FuncParameter cast = null; for (ParseTree pathTree : castFunctionContext.children) { if (pathTree instanceof AqlParser.IdentifiedPathContext) { @@ -263,13 +274,26 @@ private void handleCastFunctionDefinition( variableDefinition.setAlias("_FCT_ARG_" + serial++); } pushVariableDefinition(variableDefinition); - parameters.add(new FuncParameter( + FuncParameter funcParameter = new FuncParameter( FuncParameterType.VARIABLE, variableDefinition.getAlias() == null ? variableDefinition.getPath() - : variableDefinition.getAlias())); - } else if (pathTree instanceof AqlParser.OperandContext) { - parameters.add(new FuncParameter(FuncParameterType.OPERAND, pathTree.getText())); + : variableDefinition.getAlias()); + parameters.add(funcParameter); + cast = funcParameter; + } else if (pathTree instanceof AqlParser.OperandContext operandContext) { + FuncParameter funcParameter = + new FuncParameter(FuncParameterType.OPERAND, handleOperator(operandContext)); + cast = funcParameter; + parameters.add(funcParameter); + } else if (pathTree instanceof AqlParser.FunctionContext functionContextInner) { + + FuncParameter funcParameter = new FuncParameter( + FuncParameterType.FUNCTION, + handleFunctionDefinitionInner(functionContextInner, inSelectExprContext)); + cast = funcParameter; + parameters.add(funcParameter); + } else if (pathTree instanceof TerminalNode) { String text = pathTree.getText(); if (text.contains("'")) { @@ -286,7 +310,12 @@ private void handleCastFunctionDefinition( alias = "CAST"; } String path = castFunctionContext.getText(); - FunctionDefinition definition = new FunctionDefinition("CAST", alias, path, parameters); + FunctionDefinition definition = new CastFunctionDefinition( + alias, + path, + parameters, + cast, + StringUtils.strip(castFunctionContext.STRING().getText(), "'")); pushVariableDefinition(definition); } @@ -313,9 +342,9 @@ public void handleTerminalNodeExpression( } else if (inStdExpressionContext.TRUE() != null) { value = true; } else if (inStdExpressionContext.FLOAT() != null) { - value = Float.valueOf(inStdExpressionContext.getText()); + value = Double.valueOf(inStdExpressionContext.getText()); } else if (inStdExpressionContext.INTEGER() != null) { - value = Integer.valueOf(inStdExpressionContext.getText()); + value = Long.valueOf(inStdExpressionContext.getText()); } else if (inStdExpressionContext.NULL() != null) { value = null; } else if (inStdExpressionContext.REAL() != null) { @@ -333,6 +362,34 @@ public void handleTerminalNodeExpression( pushVariableDefinition(definition); } + private Object handleOperator(AqlParser.OperandContext operandContext) { + Object value; + if (operandContext.BOOLEAN() != null) { + value = Boolean.valueOf(operandContext.getText()); + } else if (operandContext.FALSE() != null) { + value = false; + } else if (operandContext.TRUE() != null) { + value = true; + } else if (operandContext.FLOAT() != null) { + value = Float.valueOf(operandContext.getText()); + } else if (operandContext.INTEGER() != null) { + value = Integer.valueOf(operandContext.getText()); + } else if (operandContext.NULL() != null) { + value = null; + } else if (operandContext.REAL() != null) { + value = Double.valueOf(operandContext.getText()); + } else if (operandContext.UNKNOWN() != null) { + value = null; + } else if (operandContext.STRING() != null) { + value = StringUtils.strip(operandContext.getText(), "'"); + } else // DATE() + { + value = operandContext.getText(); + } + + return value; + } + /** * check if a variable definition is unique (e.g. new aliase) * diff --git a/service/src/main/java/org/ehrbase/aql/definition/CastFunctionDefinition.java b/service/src/main/java/org/ehrbase/aql/definition/CastFunctionDefinition.java new file mode 100644 index 000000000..5b30cdbbb --- /dev/null +++ b/service/src/main/java/org/ehrbase/aql/definition/CastFunctionDefinition.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 vitasystems GmbH and Hannover Medical School. + * + * This file is part of project EHRbase + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.ehrbase.aql.definition; + +import java.util.List; + +/** + * @author Stefan Spiska + */ +public class CastFunctionDefinition extends FunctionDefinition { + + private final FuncParameter castee; + private final String targetType; + + public CastFunctionDefinition( + String alias, String path, List parameters, FuncParameter castee, String targetType) { + super("CAST", alias, path, parameters); + this.castee = castee; + this.targetType = targetType; + } + + public FuncParameter getCastee() { + return castee; + } + + public String getTargetType() { + return targetType; + } +} diff --git a/service/src/main/java/org/ehrbase/aql/definition/FuncParameter.java b/service/src/main/java/org/ehrbase/aql/definition/FuncParameter.java index 3e2ade56a..0cf4d83be 100644 --- a/service/src/main/java/org/ehrbase/aql/definition/FuncParameter.java +++ b/service/src/main/java/org/ehrbase/aql/definition/FuncParameter.java @@ -23,9 +23,9 @@ public class FuncParameter { private FuncParameterType type; - private String value; + private Object value; - public FuncParameter(FuncParameterType type, String value) { + public FuncParameter(FuncParameterType type, Object value) { this.type = type; this.value = value; } @@ -34,7 +34,7 @@ public FuncParameterType getType() { return type; } - public String getValue() { + public Object getValue() { return value; } diff --git a/service/src/main/java/org/ehrbase/aql/definition/FuncParameterType.java b/service/src/main/java/org/ehrbase/aql/definition/FuncParameterType.java index 2427b208d..e9c0bd92c 100644 --- a/service/src/main/java/org/ehrbase/aql/definition/FuncParameterType.java +++ b/service/src/main/java/org/ehrbase/aql/definition/FuncParameterType.java @@ -23,5 +23,6 @@ public enum FuncParameterType { VARIABLE, OPERAND, - IDENTIFIER + IDENTIFIER, + FUNCTION } diff --git a/service/src/main/java/org/ehrbase/aql/sql/QueryProcessor.java b/service/src/main/java/org/ehrbase/aql/sql/QueryProcessor.java index 65662884f..6c20e3857 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/QueryProcessor.java +++ b/service/src/main/java/org/ehrbase/aql/sql/QueryProcessor.java @@ -413,7 +413,9 @@ private List> buildExplain(Select select) { List details = new ArrayList<>(); details.add(sql); for (Param parameter : select.getParams().values()) { - details.add(parameter.getValue().toString()); + if (!parameter.isInline()) { + details.add(parameter.getValue().toString()); + } } explainList.add(details); return explainList; diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/ConstantField.java b/service/src/main/java/org/ehrbase/aql/sql/binding/ConstantField.java index 85db51f7f..825a965e9 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/ConstantField.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/ConstantField.java @@ -36,9 +36,11 @@ Field toSql() { Field field; ConstantDefinition constantDefinition = (ConstantDefinition) variableDefinition; - if (constantDefinition.getValue() == null) // assume NULL - field = DSL.field("NULL"); - else field = DSL.field(DSL.val(constantDefinition.getValue())); + if (constantDefinition.getValue() == null) { // assume NULL + field = DSL.inline((Object) null); + } else { + field = DSL.inline(constantDefinition.getValue()); + } if (constantDefinition.getAlias() != null) field = field.as(constantDefinition.getAlias()); else { diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/FieldConstantHandler.java b/service/src/main/java/org/ehrbase/aql/sql/binding/FieldConstantHandler.java index d7db0f53b..a95c74ad4 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/FieldConstantHandler.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/FieldConstantHandler.java @@ -51,7 +51,7 @@ public Field field() { List segments = LocatableHelper.dividePathIntoSegments(variableDefinition.getPath()); if (segments.get(segments.size() - 1).equals(I_DvTypeAdapter.ARCHETYPE_NODE_ID)) - return DSL.field(DSL.val(implicitArchetypeNodeId(segments.get(segments.size() - 2)))) + return DSL.inline(implicitArchetypeNodeId(segments.get(segments.size() - 2))) .as(alias()); return null; } diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/FunctionExpression.java b/service/src/main/java/org/ehrbase/aql/sql/binding/FunctionExpression.java index 3e00d9f46..2756e4643 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/FunctionExpression.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/FunctionExpression.java @@ -19,8 +19,15 @@ import java.util.ArrayList; import java.util.List; +import org.ehrbase.aql.definition.CastFunctionDefinition; import org.ehrbase.aql.definition.FuncParameter; +import org.ehrbase.aql.definition.FuncParameterType; import org.ehrbase.aql.definition.I_VariableDefinition; +import org.jooq.DataType; +import org.jooq.Field; +import org.jooq.SelectQuery; +import org.jooq.impl.DSL; +import org.jooq.impl.SQLDataType; /** * handles function expression and parameters. @@ -28,33 +35,79 @@ public class FunctionExpression { private final I_VariableDefinition functionDefinition; + private final SelectQuery query; private final VariableDefinitions variables; - FunctionExpression(VariableDefinitions variables, I_VariableDefinition functionDefinition) { + FunctionExpression(VariableDefinitions variables, I_VariableDefinition functionDefinition, SelectQuery query) { this.variables = variables; this.functionDefinition = functionDefinition; + this.query = query; } - public String toString() { + public Field buildField() { - StringBuilder expression = new StringBuilder(); + if (functionDefinition instanceof CastFunctionDefinition castFunctionDefinition) { + return to(castFunctionDefinition.getCastee()).cast(getType(castFunctionDefinition.getTargetType())); + } - for (FuncParameter parameter : functionDefinition.getFuncParameters()) { - if (parameter.isVariable()) { - if (variables.isDistinct(parameter.getValue())) expression.append("DISTINCT "); - expression.append("\""); - expression.append(parameter.getValue()); - expression.append("\""); - } else expression.append(parameter.getValue()); + if (isAggregateDistinct()) { + return DSL.aggregateDistinct( + functionDefinition.getIdentifier(), + Object.class, + functionDefinition.getFuncParameters().stream() + .filter(funcParameter -> !funcParameter.isIdentifier()) + .map(this::to) + .toArray(i -> new Field[i])); + } else { + return DSL.function( + functionDefinition.getIdentifier(), + Object.class, + functionDefinition.getFuncParameters().stream() + .filter(funcParameter -> !funcParameter.isIdentifier()) + .map(this::to) + .toArray(i -> new Field[i])); } - return expression.toString(); + } + + private DataType getType(String as) { + + return switch (as.toUpperCase()) { + case "DATE" -> SQLDataType.DATE; + case "TIME" -> SQLDataType.TIME; + case "INTERVAL" -> SQLDataType.INTERVAL; + case "TIMESTAMP" -> SQLDataType.TIMESTAMP; + case "NUMERIC" -> SQLDataType.NUMERIC; + default -> SQLDataType.VARCHAR; + }; + } + + private boolean isAggregateDistinct() { + return functionDefinition.getFuncParameters().stream() + .filter(FuncParameter::isVariable) + .anyMatch(f -> variables.isDistinct(f.getValue().toString())); + } + + private Field to(FuncParameter funcParameter) { + + return switch (funcParameter.getType()) { + case VARIABLE -> OrderByBinder.find(query, funcParameter.getValue().toString()); + case OPERAND -> DSL.inline(funcParameter.getValue()); + case IDENTIFIER -> throw new UnsupportedOperationException( + funcParameter.getType().toString()); + case FUNCTION -> new FunctionExpression(variables, (I_VariableDefinition) funcParameter.getValue(), query) + .buildField(); + }; } List arguments() { List args = new ArrayList<>(); for (FuncParameter parameter : functionDefinition.getFuncParameters()) { - if (parameter.isVariable()) args.add(parameter.getValue()); + if (parameter.isVariable()) args.add(parameter.getValue().toString()); + if (parameter.getType().equals(FuncParameterType.FUNCTION)) { + args.addAll(new FunctionExpression(variables, (I_VariableDefinition) parameter.getValue(), query) + .arguments()); + } } return args; diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/JoinBinder.java b/service/src/main/java/org/ehrbase/aql/sql/binding/JoinBinder.java index daaa84250..d2b2bf239 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/JoinBinder.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/JoinBinder.java @@ -28,7 +28,6 @@ import org.jooq.JoinType; import org.jooq.SelectQuery; import org.jooq.Table; -import org.jooq.impl.DSL; /** * Created by christian on 10/31/2016. @@ -152,7 +151,7 @@ private void joinComposition(SelectQuery selectQuery) { selectQuery.addJoin( compositionRecordTable, JoinType.RIGHT_OUTER_JOIN, - DSL.field(compositionRecordTable.field(COMPOSITION.ID)).eq(ENTRY.COMPOSITION_ID)); + compositionRecordTable.field(COMPOSITION.ID).eq(ENTRY.COMPOSITION_ID)); compositionJoined = true; } @@ -161,8 +160,7 @@ private void joinSystem(SelectQuery selectQuery) { selectQuery.addJoin( systemRecordTable, JoinType.JOIN, - DSL.field(systemRecordTable.field(SYSTEM.ID)) - .eq(DSL.field(ehrRecordTable.field(EHR_.SYSTEM_ID.getName(), UUID.class)))); + systemRecordTable.field(SYSTEM.ID).eq(ehrRecordTable.field(EHR_.SYSTEM_ID.getName(), UUID.class))); systemJoined = true; } @@ -172,15 +170,17 @@ private void joinEhrStatus(SelectQuery selectQuery, JoinSetup joinSetup) { joinComposition(selectQuery); selectQuery.addJoin( statusRecordTable, - DSL.field(statusRecordTable.field(STATUS.EHR_ID.getName(), UUID.class)) - .eq(DSL.field(compositionRecordTable.field(COMPOSITION.EHR_ID.getName(), UUID.class)))); + statusRecordTable + .field(STATUS.EHR_ID.getName(), UUID.class) + .eq(compositionRecordTable.field(COMPOSITION.EHR_ID.getName(), UUID.class))); statusJoined = true; } else { // assume it is joined on EHR if (joinSetup.isJoinEhr()) joinEhr(selectQuery); selectQuery.addJoin( statusRecordTable, - DSL.field(statusRecordTable.field(STATUS.EHR_ID.getName(), UUID.class)) - .eq(DSL.field(ehrRecordTable.field(EHR_.ID.getName(), UUID.class)))); + statusRecordTable + .field(STATUS.EHR_ID.getName(), UUID.class) + .eq(ehrRecordTable.field(EHR_.ID.getName(), UUID.class))); statusJoined = true; } } @@ -191,8 +191,9 @@ private void joinSubject(SelectQuery selectQuery, JoinSetup joinSetup) { Table subjectTable = subjectRef; selectQuery.addJoin( subjectTable, - DSL.field(subjectTable.field(PARTY_IDENTIFIED.ID.getName(), UUID.class)) - .eq(DSL.field(statusRecordTable.field(STATUS.PARTY.getName(), UUID.class)))); + subjectTable + .field(PARTY_IDENTIFIED.ID.getName(), UUID.class) + .eq(statusRecordTable.field(STATUS.PARTY.getName(), UUID.class))); subjectJoin = true; } @@ -209,7 +210,7 @@ private void joinContextFacility(SelectQuery selectQuery) { selectQuery.addJoin( facilityTable, JoinType.LEFT_OUTER_JOIN, - EVENT_CONTEXT.FACILITY.eq(DSL.field(facilityTable.field(PARTY_IDENTIFIED.ID.getName(), UUID.class)))); + EVENT_CONTEXT.FACILITY.eq(facilityTable.field(PARTY_IDENTIFIED.ID.getName(), UUID.class))); facilityJoined = true; } @@ -219,8 +220,9 @@ private void joinComposer(SelectQuery selectQuery) { Table composerTable = composerRef; selectQuery.addJoin( composerTable, - DSL.field(compositionRecordTable.field(COMPOSITION.COMPOSER.getName(), UUID.class)) - .eq(DSL.field(composerTable.field(PARTY_IDENTIFIED.ID.getName(), UUID.class)))); + compositionRecordTable + .field(COMPOSITION.COMPOSER.getName(), UUID.class) + .eq(composerTable.field(PARTY_IDENTIFIED.ID.getName(), UUID.class))); composerJoined = true; } @@ -230,8 +232,9 @@ private void joinEhr(SelectQuery selectQuery) { selectQuery.addJoin( ehrRecordTable, JoinType.RIGHT_OUTER_JOIN, - DSL.field(ehrRecordTable.field(EHR_.ID.getName(), UUID.class)) - .eq(DSL.field(compositionRecordTable.field(COMPOSITION.EHR_ID.getName(), UUID.class)))); + ehrRecordTable + .field(EHR_.ID.getName(), UUID.class) + .eq(compositionRecordTable.field(COMPOSITION.EHR_ID.getName(), UUID.class))); ehrJoined = true; } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/LateralJoins.java b/service/src/main/java/org/ehrbase/aql/sql/binding/LateralJoins.java index 29f178a31..f33a1c40d 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/LateralJoins.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/LateralJoins.java @@ -87,7 +87,7 @@ public static void create( String variableAlias = "var_" + abs + "_" + inc(); SelectSelectStep wrappedSelectSelectStep = - DSL.select(DSL.field(selectSelectStep).as(variableAlias)); + DSL.select(selectSelectStep.asField().as(variableAlias)); Table table = DSL.table(wrappedSelectSelectStep).as(tableAlias); item.setLateralJoinTable( diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/OrderByBinder.java b/service/src/main/java/org/ehrbase/aql/sql/binding/OrderByBinder.java index f1aad14ab..13cb4e1ba 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/OrderByBinder.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/OrderByBinder.java @@ -22,12 +22,13 @@ import java.util.Iterator; import java.util.List; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.ehrbase.aql.compiler.OrderAttribute; import org.ehrbase.aql.definition.I_VariableDefinition; +import org.jooq.Field; import org.jooq.Record; import org.jooq.SelectQuery; import org.jooq.SortField; -import org.jooq.impl.DSL; /** * Created by christian on 9/23/2016. @@ -46,13 +47,13 @@ public class OrderByBinder { this.variableDefinitions = variableDefinitions; } - List> getOrderByFields() { + List> getOrderByFields() { if (orderAttributes.isEmpty()) return Collections.emptyList(); - List> orderFields = new ArrayList<>(); + List> orderFields = new ArrayList<>(); for (OrderAttribute orderAttribute : orderAttributes) { - SortField field; + SortField field; String fieldIdentifier = null; // get the corresponding variable definition @@ -80,20 +81,26 @@ List> getOrderByFields() { orderAttribute.getVariableDefinition().setHidden(true); } - if (!fieldIdentifier.startsWith("\"")) - fieldIdentifier = "\"" + fieldIdentifier + "\""; // by postgresql convention - if (orderAttribute.getDirection() != null && orderAttribute.getDirection().equals(OrderAttribute.OrderDirection.DESC)) { - field = DSL.field(fieldIdentifier).desc(); + field = find(select, fieldIdentifier).desc(); } else // default to ASCENDING - field = DSL.field(fieldIdentifier).asc(); + field = find(select, fieldIdentifier).asc(); orderFields.add(field); } + return orderFields; } + public static Field find(SelectQuery selectQuery, String fieldIdentifier) { + return selectQuery.getSelect().stream() + .filter(a -> StringUtils.strip(a.getUnqualifiedName().toString(), "\"") + .equals(fieldIdentifier)) + .findFirst() + .orElseThrow(); + } + public SelectQuery bind() { if (CollectionUtils.isNotEmpty(orderAttributes)) { diff --git a/service/src/main/java/org/ehrbase/aql/sql/binding/SuperQuery.java b/service/src/main/java/org/ehrbase/aql/sql/binding/SuperQuery.java index bb08875f4..0c44f3459 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/binding/SuperQuery.java +++ b/service/src/main/java/org/ehrbase/aql/sql/binding/SuperQuery.java @@ -116,8 +116,9 @@ private SelectQuery selectAggregate(SelectQuery selectQuery) { if (variableDefinition.isFunction()) { skipField.add(alias); - FunctionExpression functionExpression = new FunctionExpression(variableDefinitions, variableDefinition); - Field field = field(functionExpression.toString()); + FunctionExpression functionExpression = + new FunctionExpression(variableDefinitions, variableDefinition, this.query); + Field field = functionExpression.buildField(); skipField.addAll(functionExpression.arguments()); if (variableDefinition.getAlias() != null) { diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/AqlRoutines.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/AqlRoutines.java index 304447a87..dc43da0a7 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/AqlRoutines.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/AqlRoutines.java @@ -18,10 +18,9 @@ package org.ehrbase.aql.sql.queryimpl; import static org.ehrbase.aql.sql.queryimpl.QueryImplConstants.AQL_NODE_ITERATIVE_FUNCTION; -import static org.ehrbase.aql.sql.queryimpl.QueryImplConstants.AQL_NODE_NAME_PREDICATE_MARKER; import java.util.Arrays; -import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.jooq.Configuration; import org.jooq.Field; @@ -35,45 +34,46 @@ private AqlRoutines() { } public static Field jsonArraySplitElements(Field jsonbVal) { - return DSL.field(AQL_NODE_ITERATIVE_FUNCTION + "(" + jsonbVal + ")").cast(JSONB.class); + + return DSL.function(AQL_NODE_ITERATIVE_FUNCTION, JSONB.class, jsonbVal); } public static Field jsonArraySplitElements(Configuration configuration, Field jsonbVal) { isSupported(configuration); - return DSL.field(AQL_NODE_ITERATIVE_FUNCTION + "(" + jsonbVal + ")").cast(JSONB.class); + + return jsonArraySplitElements(jsonbVal); } public static Field jsonpathItem(Field jsonbVal, String[] elements) { - return DSL.field("jsonb_extract_path(" + jsonbVal + "," + String.join(",", elements) + ")") - .cast(JSONB.class); + + return DSL.function("jsonb_extract_path", JSONB.class, ArrayUtils.addFirst(buildParameter(elements), jsonbVal)); } public static Field jsonpathItem(Configuration configuration, Field jsonbVal, String[] elements) { isSupported(configuration); - return DSL.field("jsonb_extract_path(" + jsonbVal + "," + String.join(",", elements) + ")") - .cast(JSONB.class); + return jsonpathItem(jsonbVal, elements); } - public static Field toJson(Configuration configuration, String expression) { - isSupported(configuration); - return DSL.field("to_json(" + expression + ")").cast(JSONB.class); - } + public static Field jsonpathItemAsText(Field jsonbVal, String[] elements) { - public static String jsonpathItemAsText(Field jsonbVal, String[] elements) { - return "jsonb_extract_path_text(" + jsonbVal + "," + String.join(",", elements) + ")"; + return DSL.function( + "jsonb_extract_path_text", String.class, ArrayUtils.addFirst(buildParameter(elements), jsonbVal)); } - public static String jsonpathItemAsText(Configuration configuration, Field jsonbVal, String[] elements) { + public static Field jsonpathItemAsText( + Configuration configuration, Field jsonbVal, String[] elements) { isSupported(configuration); - return "jsonb_extract_path_text(" + jsonbVal + "," + String.join(",", elements) + ")"; + return jsonpathItemAsText(jsonbVal, elements); + } + + private static Field[] buildParameter(String[] elements) { + return Arrays.stream(elements).map(DSL::inline).toArray(Field[]::new); } public static String[] jsonpathParameters(String rawParameters) { String parametersFormatted = StringUtils.remove(StringUtils.remove(rawParameters, "'{"), "}'"); return Arrays.stream(parametersFormatted.split(",")) .map(s -> (s.startsWith("'") ? s.replace("'", "") : s)) - .map(s -> (!s.equals(AQL_NODE_NAME_PREDICATE_MARKER) ? "'" + s + "'" : s)) - .collect(Collectors.toList()) - .toArray(new String[] {}); + .toArray(String[]::new); } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/CompositionAttributeQuery.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/CompositionAttributeQuery.java index 8b8bba546..e5ffd111e 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/CompositionAttributeQuery.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/CompositionAttributeQuery.java @@ -133,7 +133,9 @@ else if (columnAlias.startsWith("context")) .getTable() .getName() + "." + variableDefinition.getSubstituteFieldVariable(); retField = DSL.field(sqlToLateralJoin).as(retField.getName()); - } else if (fieldResolutionContext.isUsingSetReturningFunction()) retField = DSL.field(DSL.select(retField)); + } else if (fieldResolutionContext.isUsingSetReturningFunction()) { + retField = DSL.select(retField).asField(); + } QualifiedAqlField aqlField = new QualifiedAqlField(retField); diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCall.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCall.java index b2078484a..f32c1707f 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCall.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCall.java @@ -28,6 +28,7 @@ import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.ehrbase.aql.sql.queryimpl.attribute.FieldResolutionContext; +import org.ehrbase.aql.sql.queryimpl.value_field.Functions; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.JSONB; @@ -77,7 +78,7 @@ private List resolveNodePredicateCall( Field nodeField; if (!itemPathArray.get(0).replace("\"", "").startsWith(QueryImplConstants.AQL_NODE_NAME_PREDICATE_FUNCTION)) { - nodeField = DSL.field(apply(function, tableFields).toString()).cast(JSONB.class); + nodeField = Functions.inline(apply(function, tableFields)).cast(JSONB.class); startList = 0; } else { nodeField = DSL.field(itemPathArray.get(0)); @@ -91,12 +92,12 @@ private List resolveNodePredicateCall( markerPos = itemPathArray.size(); expression.add(aqlNodeNamePredicate( - DSL.field(jsonpathItemAsText( + jsonpathItemAsText( configuration, nodeField, itemPathArray .subList(startList, markerPos) - .toArray(new String[] {}))) + .toArray(new String[] {})) .cast(JSONB.class), DSL.val(itemPathArray.get(markerPos + 1).replace("'", "")), DSL.val("")) @@ -113,8 +114,8 @@ private List resolveNodePredicateCall( if (!expression.contains(QueryImplConstants.AQL_NODE_NAME_PREDICATE_MARKER) && extractPathArguments.length > 0) { List resultList = new ArrayList<>(); - resultList.add(DSL.field(jsonpathItemAsText( - configuration, DSL.field(expression.get(0)).cast(JSONB.class), extractPathArguments)) + resultList.add(jsonpathItemAsText( + configuration, DSL.field(expression.get(0)).cast(JSONB.class), extractPathArguments) .toString()); expression = resultList; } @@ -127,7 +128,7 @@ private String[] rightPathExpression(List itemPathArray, int from, int t .subList( // test if the starting item is an index, then skip it as it is mutually exclusive with node // name predicate node selection - itemPathArray.get(from + 2).matches("'[0-9]*'|#") ? from + 3 : from + 2, to) + itemPathArray.get(from + 2).matches("[0-9]*|#") ? from + 3 : from + 2, to) .toArray(new String[] {}); } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/JsonbEntryQuery.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/JsonbEntryQuery.java index e83438c66..b599870f5 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/JsonbEntryQuery.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/JsonbEntryQuery.java @@ -234,7 +234,7 @@ public MultiFields makeField( } else if (clause.equals(Clause.WHERE)) { fieldPathItem = buildFieldWithCast(itemPath, castTypeAs, null); if (itemPathArray.contains(AQL_NODE_ITERATIVE_MARKER)) - fieldPathItem = DSL.field(DSL.select(fieldPathItem)); + fieldPathItem = DSL.select(fieldPathItem).asField(); } if (setReturningFunctionInWhere) diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/NullField.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/NullField.java index c8263cdee..8e70960c2 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/NullField.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/NullField.java @@ -34,15 +34,21 @@ public NullField(I_VariableDefinition variableDefinition, String alias) { } public Field instance() { + // return a null field - String cast = ""; + Field nullField = DSL.inline((Object) null); + // force explicit type cast for DvQuantity if (variableDefinition != null && variableDefinition.getPath() != null - && variableDefinition.getPath().endsWith(MAGNITUDE)) cast = "::numeric"; + && variableDefinition.getPath().endsWith(MAGNITUDE)) { + nullField = nullField.cast(Double.class); + } - if (variableDefinition != null && alias != null) - return DSL.field(DSL.val((String) null) + cast).as(alias); - else return DSL.field(DSL.val((String) null) + cast); + if (variableDefinition != null && alias != null) { + return nullField.as(alias); + } else { + return nullField; + } } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZone.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZone.java index 18b740b85..fe8d922b9 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZone.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZone.java @@ -20,7 +20,6 @@ import static org.ehrbase.aql.sql.queryimpl.AqlRoutines.jsonpathItemAsText; import static org.ehrbase.aql.sql.queryimpl.AqlRoutines.jsonpathParameters; import static org.ehrbase.jooq.pg.Routines.jsDvDateTime; -import static org.jooq.impl.DSL.field; import org.ehrbase.aql.sql.queryimpl.attribute.eventcontext.SimpleEventContextAttribute; import org.jooq.Field; @@ -38,10 +37,11 @@ public TemporalWithTimeZone(FieldResolutionContext fieldContext, JoinSetup joinS @Override public Field sqlField() { + // "ehr.js_dv_date_time("+tableField+"::timestamptz, // COALESCE("+timeZoneField+"::text,'UTC'))::json #>>'{value}'") - return as(field(jsonpathItemAsText( - jsDvDateTime(tableField, timeZoneField).cast(JSONB.class), jsonpathParameters("value")))); + return as(jsonpathItemAsText( + jsDvDateTime(tableField, timeZoneField).cast(JSONB.class), jsonpathParameters("value"))); } public TemporalWithTimeZone useTimeZone(TableField tableField) { @@ -53,8 +53,8 @@ public TemporalWithTimeZone useTimeZone(TableField tableField) { public IRMObjectAttribute forTableField(TableField tableField) { this.tableField = tableField; if (timeZoneField == null) { - String tzFieldName = tableField.getName().toUpperCase() + "_TZID"; // conventionally - timeZoneField = field(tableField.getTable().getName() + "." + tzFieldName); + String tzFieldName = tableField.getName() + "_tzid"; // conventionally + timeZoneField = tableField.getTable().field(tzFieldName); } return this; } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionName.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionName.java deleted file mode 100644 index 285fee5a3..000000000 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionName.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2019 vitasystems GmbH and Hannover Medical School. - * - * This file is part of project EHRbase - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehrbase.aql.sql.queryimpl.attribute.composition; - -import static org.ehrbase.jooq.pg.Tables.ENTRY; - -import org.ehrbase.aql.sql.queryimpl.IQueryImpl; -import org.ehrbase.aql.sql.queryimpl.attribute.FieldResolutionContext; -import org.ehrbase.aql.sql.queryimpl.attribute.IRMObjectAttribute; -import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; -import org.jooq.Field; -import org.jooq.TableField; -import org.jooq.impl.DSL; - -public class CompositionName extends CompositionAttribute { - - public CompositionName(FieldResolutionContext fieldContext, JoinSetup joinSetup) { - super(fieldContext, joinSetup); - } - - @Override - public Field sqlField() { - // extract the composition name from the jsonb root key - String trimName = "trim(LEADING '''' FROM (trim(TRAILING ''']' FROM\n" - + " (regexp_split_to_array((select root_json_key from jsonb_object_keys(" - + ENTRY.ENTRY_ + ") root_json_key where root_json_key like '/composition%')," - + " 'and name/value=')) [2])))"; - // postgresql equivalent expression - if (fieldContext.isWithAlias()) { - return aliased(DSL.field(trimName)); - } else { - if (fieldContext.getClause().equals(IQueryImpl.Clause.WHERE)) { - trimName = "(SELECT " + trimName + ")"; - } - return defaultAliased(DSL.field(trimName)); - } - } - - @Override - public IRMObjectAttribute forTableField(TableField tableField) { - return this; - } -} diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionUidValue.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionUidValue.java index 2ebe4a764..7ff2dcf94 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionUidValue.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/CompositionUidValue.java @@ -29,7 +29,6 @@ import org.jooq.SelectQuery; import org.jooq.TableField; import org.jooq.impl.DSL; -import org.jooq.impl.SQLDataType; public class CompositionUidValue extends CompositionAttribute { @@ -53,7 +52,7 @@ public IRMObjectAttribute forTableField(TableField tableField) { return this; } - private Field uid() { + private Field uid() { // use inline SQL as it seems coalesce is not going through with POSTGRES dialect SelectQuery subSelect = fieldContext.getContext().selectQuery(); @@ -63,22 +62,13 @@ private Field uid() { JoinBinder.compositionRecordTable.field("id", UUID.class).eq(COMPOSITION_HISTORY.ID)); subSelect.addGroupBy(COMPOSITION_HISTORY.ID); - String coalesceVersion = "1 + COALESCE(\n(" + subSelect + "), 0)"; + Field version = DSL.inline(1).plus(DSL.function("COALESCE", Integer.class, subSelect.asField())); - return aliased(DSL.field( - JoinBinder.compositionRecordTable.field("id") - + "||" - + DSL.val("::") - + "||" - + DSL.val(fieldContext.getServerNodeId()) - + "||" - + DSL.val("::") - + "||" - + DSL.field(coalesceVersion), - SQLDataType.VARCHAR)); + return DSL.concat( + rawUid(), DSL.inline("::"), DSL.inline(fieldContext.getServerNodeId()), DSL.inline("::"), version); } private Field rawUid() { - return as(DSL.field(JoinBinder.compositionRecordTable.field("id", UUID.class))); + return as(JoinBinder.compositionRecordTable.field("id", UUID.class)); } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/FullCompositionJson.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/FullCompositionJson.java index 099c4a5c9..ed2e76932 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/FullCompositionJson.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/FullCompositionJson.java @@ -55,22 +55,20 @@ public Field sqlField() { Field jsonFullComposition; if (jsonPath.isPresent()) { - jsonFullComposition = DSL.field(jsonpathItemAsText( + jsonFullComposition = jsonpathItemAsText( configuration, jsComposition2( - DSL.field(JoinBinder.compositionRecordTable.getName() + "." + tableField.getName()) - .cast(UUID.class), - DSL.val(fieldContext.getServerNodeId())) + JoinBinder.compositionRecordTable.field(tableField.getName(), UUID.class), + DSL.inline(fieldContext.getServerNodeId())) .cast(JSONB.class), - jsonpathParameters(jsonPath.get()))); + jsonpathParameters(jsonPath.get())); } else - jsonFullComposition = DSL.field(jsComposition2( - DSL.field(JoinBinder.compositionRecordTable.getName() + "." + tableField.getName()) - .cast(UUID.class), - DSL.val(fieldContext.getServerNodeId())) - .cast(String.class)); + jsonFullComposition = jsComposition2( + JoinBinder.compositionRecordTable.field(tableField.getName(), UUID.class), + DSL.inline(fieldContext.getServerNodeId())) + .cast(String.class); - if (fieldContext.isWithAlias()) return aliased(DSL.field(jsonFullComposition)); + if (fieldContext.isWithAlias()) return aliased(jsonFullComposition); else return defaultAliased(jsonFullComposition); } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/SimpleCompositionAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/SimpleCompositionAttribute.java index 6a828b3d0..c7b70f731 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/SimpleCompositionAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/composition/SimpleCompositionAttribute.java @@ -25,7 +25,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SimpleCompositionAttribute extends CompositionAttribute { @@ -38,10 +37,10 @@ public SimpleCompositionAttribute(FieldResolutionContext fieldContext, JoinSetup @Override public Field sqlField() { - Field actualField = DSL.field(tableField); + Field actualField = tableField; if (tableField.getTable().equals(COMPOSITION)) { - actualField = DSL.field(JoinBinder.compositionRecordTable.getName() + "." + tableField.getName()); + actualField = JoinBinder.compositionRecordTable.field(tableField); } return as(actualField); diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/EhrIdValue.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/EhrIdValue.java index 2080cc52c..0bd9e9618 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/EhrIdValue.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/EhrIdValue.java @@ -25,7 +25,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; public class EhrIdValue extends EhrAttribute { @@ -40,19 +39,17 @@ public Field sqlField() { if (fieldContext.getPathResolver().hasPathExpression()) { joinSetup.setJoinEhr(true); if (fieldContext.isWithAlias()) { - return aliased(DSL.field("{0}", JoinBinder.ehrRecordTable.field(EHR_.ID.getName()))); - } else - return defaultAliased( - DSL.field(JoinBinder.ehrRecordTable.field(JoinBinder.ehrRecordTable.field(EHR_.ID.getName())))); + return aliased(JoinBinder.ehrRecordTable.field(EHR_.ID)); + } else return defaultAliased(JoinBinder.ehrRecordTable.field(JoinBinder.ehrRecordTable.field(EHR_.ID))); } else if (!joinSetup.isContainsEhrStatus()) { joinSetup.setJoinEhr(true); if (fieldContext.isWithAlias()) { - return aliased(DSL.field("{0}", JoinBinder.ehrRecordTable.field(EHR_.ID.getName()))); - } else return defaultAliased(DSL.field(JoinBinder.ehrRecordTable.field(EHR_.ID.getName()))); + return aliased(JoinBinder.ehrRecordTable.field(EHR_.ID)); + } else return defaultAliased(JoinBinder.ehrRecordTable.field(EHR_.ID)); } else { if (fieldContext.isWithAlias()) { - return aliased(DSL.field("{0}", JoinBinder.ehrRecordTable.field(EHR_.ID.getName()))); - } else return defaultAliased(DSL.field(JoinBinder.ehrRecordTable.field(EHR_.ID.getName()))); + return aliased(JoinBinder.ehrRecordTable.field(EHR_.ID)); + } else return defaultAliased(JoinBinder.ehrRecordTable.field(EHR_.ID)); } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/FullEhrJson.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/FullEhrJson.java index b67278918..43845ea80 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/FullEhrJson.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/FullEhrJson.java @@ -75,50 +75,39 @@ public Field sqlField() { } if (prefix != null) { - jsonFullEhr = DSL.field(jsonpathItemAsText( + jsonFullEhr = jsonpathItemAsText( configuration, jsonArraySplitElements( configuration, jsonpathItem( configuration, jsEhr( - DSL.field(JoinBinder.ehrRecordTable - .getName() - .concat(".") - .concat(tableField.getName())) - .cast(UUID.class), - DSL.val(fieldContext.getServerNodeId())) + JoinBinder.ehrRecordTable.field( + tableField.getName(), UUID.class), + DSL.inline(fieldContext.getServerNodeId())) .cast(JSONB.class), prefix)), - suffix)); + suffix); if (fieldContext.getClause().equals(IQueryImpl.Clause.WHERE)) - jsonFullEhr = DSL.field(DSL.select(jsonFullEhr)); + jsonFullEhr = DSL.select(jsonFullEhr).asField(); } else { if (jsonPath.isPresent()) - jsonFullEhr = DSL.field(jsonpathItem( + jsonFullEhr = jsonpathItem( configuration, jsEhr( - DSL.field(JoinBinder.ehrRecordTable - .getName() - .concat(".") - .concat(tableField.getName())) - .cast(UUID.class), - DSL.val(fieldContext.getServerNodeId())) + JoinBinder.ehrRecordTable.field(tableField.getName(), UUID.class), + DSL.inline(fieldContext.getServerNodeId())) .cast(JSONB.class), - jsonpathParameters(jsonPath.get()))); + jsonpathParameters(jsonPath.get())); } } else - jsonFullEhr = DSL.field(jsEhr( - DSL.field(JoinBinder.ehrRecordTable - .getName() - .concat(".") - .concat(tableField.getName())) - .cast(UUID.class), - DSL.val(fieldContext.getServerNodeId())) - .cast(String.class)); - - if (fieldContext.isWithAlias()) return aliased(DSL.field(jsonFullEhr)); + jsonFullEhr = jsEhr( + JoinBinder.ehrRecordTable.field(tableField.getName(), UUID.class), + DSL.inline(fieldContext.getServerNodeId())) + .cast(String.class); + + if (fieldContext.isWithAlias()) return aliased(jsonFullEhr); else return defaultAliased(jsonFullEhr); } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/EhrStatusJson.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/EhrStatusJson.java index f19e2f568..8db65250a 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/EhrStatusJson.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/EhrStatusJson.java @@ -56,23 +56,24 @@ public Field sqlField() { .ehrStatus(JoinBinder.statusRecordTable.field(STATUS.EHR_ID)); else if (jsonPath.get().contains("uid")) { // this is required since jOOQ doesn't allow (easily) to convert a Field to a TableField - jsonEhrStatusField = DSL.field(jsonpathItemAsText( + jsonEhrStatusField = jsonpathItemAsText( fieldContext.getContext().configuration(), jsEhrStatus2( JoinBinder.statusRecordTable.field(STATUS.EHR_ID), - DSL.val(fieldContext.getServerNodeId())) + DSL.inline(fieldContext.getServerNodeId())) .cast(JSONB.class), - jsonpathParameters(jsonPath.get().replace("/", ",")))); + jsonpathParameters(jsonPath.get().replace("/", ","))); } else jsonEhrStatusField = new GenericJsonField(fieldContext, joinSetup) .forJsonPath(jsonPath.get()) .ehrStatus(JoinBinder.statusRecordTable.field(STATUS.EHR_ID)); } else - jsonEhrStatusField = DSL.field(jsEhrStatus2( - JoinBinder.statusRecordTable.field(STATUS.EHR_ID), DSL.val(fieldContext.getServerNodeId())) - .cast(String.class)); + jsonEhrStatusField = jsEhrStatus2( + JoinBinder.statusRecordTable.field(STATUS.EHR_ID), + DSL.inline(fieldContext.getServerNodeId())) + .cast(String.class); - return as(DSL.field(jsonEhrStatusField)); + return as(jsonEhrStatusField); } @Override diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/SimpleEhrStatusAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/SimpleEhrStatusAttribute.java index c9ba08510..5d05bc831 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/SimpleEhrStatusAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/ehr/ehrstatus/SimpleEhrStatusAttribute.java @@ -25,7 +25,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SimpleEhrStatusAttribute extends EhrStatusAttribute { @@ -38,7 +37,7 @@ public SimpleEhrStatusAttribute(FieldResolutionContext fieldContext, JoinSetup j @Override public Field sqlField() { - return as(DSL.field(tableField)); + return as(tableField); } @Override diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/EventContextJson.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/EventContextJson.java index b2be879b4..82fe1f27c 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/EventContextJson.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/EventContextJson.java @@ -26,7 +26,6 @@ import org.ehrbase.aql.sql.queryimpl.value_field.GenericJsonField; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; public class EventContextJson extends EventContextAttribute { @@ -47,7 +46,7 @@ public Field sqlField() { .eventContext(EVENT_CONTEXT.ID); else jsonEventContext = new GenericJsonField(fieldContext, joinSetup).eventContext(EVENT_CONTEXT.ID); - if (fieldContext.isWithAlias()) return aliased(DSL.field(jsonEventContext)); + if (fieldContext.isWithAlias()) return aliased(jsonEventContext); else return defaultAliased(jsonEventContext); } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/SimpleEventContextAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/SimpleEventContextAttribute.java index 8f7a880a2..066a98148 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/SimpleEventContextAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/eventcontext/SimpleEventContextAttribute.java @@ -25,7 +25,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SimpleEventContextAttribute extends EventContextAttribute { @@ -38,7 +37,7 @@ public SimpleEventContextAttribute(FieldResolutionContext fieldContext, JoinSetu @Override public Field sqlField() { - return as(DSL.field(tableField)); + return as(tableField); } @Override diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/partyref/SimplePartyRefAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/partyref/SimplePartyRefAttribute.java index 8ad00ebd4..557b4c158 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/partyref/SimplePartyRefAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/partyref/SimplePartyRefAttribute.java @@ -22,7 +22,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SimplePartyRefAttribute extends PartyRefAttribute { @@ -35,7 +34,7 @@ public SimplePartyRefAttribute(FieldResolutionContext fieldContext, JoinSetup jo @Override public Field sqlField() { - return as(DSL.field(tableField)); + return as(tableField); } @Override diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/setting/SettingAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/setting/SettingAttribute.java index b20c408bf..e76eb05bc 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/setting/SettingAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/setting/SettingAttribute.java @@ -31,7 +31,6 @@ import org.jooq.Field; import org.jooq.JSONB; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SettingAttribute extends EventContextAttribute { @@ -52,13 +51,13 @@ public Field sqlField() { .forJsonPath(jsonPath.get()) .eventContext(EVENT_CONTEXT.ID); - Field jsonContextField = DSL.field(jsonpathItem( + Field jsonContextField = jsonpathItem( fieldContext.getContext().configuration(), Routines.jsDvCodedText2(tableField).cast(JSONB.class), - jsonpathParameters(new GenericJsonPath(jsonPath.get()).jqueryPath()))) + jsonpathParameters(new GenericJsonPath(jsonPath.get()).jqueryPath())) .cast(String.class); - return as(DSL.field(jsonContextField)); + return as(jsonContextField); } return null; } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/system/SystemAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/system/SystemAttribute.java index 35e24a559..c39def396 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/system/SystemAttribute.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/attribute/system/SystemAttribute.java @@ -24,7 +24,6 @@ import org.ehrbase.aql.sql.queryimpl.attribute.RMObjectAttribute; import org.jooq.Field; import org.jooq.TableField; -import org.jooq.impl.DSL; @SuppressWarnings({"java:S3740", "java:S1452"}) public class SystemAttribute extends RMObjectAttribute { @@ -37,7 +36,7 @@ public SystemAttribute(FieldResolutionContext fieldContext, JoinSetup joinSetup) @Override public Field sqlField() { - return as(DSL.field(JoinBinder.systemRecordTable.getName() + "." + tableField.getName())); + return as(JoinBinder.systemRecordTable.field(tableField)); } @Override diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedField.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedField.java deleted file mode 100644 index c05df9e2d..000000000 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedField.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School. - * - * This file is part of project EHRbase - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehrbase.aql.sql.queryimpl.value_field; - -import static org.ehrbase.aql.sql.queryimpl.AqlRoutines.toJson; - -import org.apache.commons.lang3.StringUtils; -import org.ehrbase.aql.sql.queryimpl.attribute.FieldResolutionContext; -import org.ehrbase.aql.sql.queryimpl.attribute.IRMObjectAttribute; -import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; -import org.ehrbase.aql.sql.queryimpl.attribute.RMObjectAttribute; -import org.jooq.Field; -import org.jooq.TableField; -import org.jooq.impl.DSL; - -/** - * use to format a result using a function (f.e. to generate a correct ISO date/time - */ -@SuppressWarnings({"java:S3776", "java:S3740"}) -public class FormattedField extends RMObjectAttribute { - - public FormattedField(FieldResolutionContext fieldContext, JoinSetup joinSetup) { - super(fieldContext, joinSetup); - } - - public Field using( - String sqlType, String separator, String resultType, String plpgsqlFunction, Field... tableFields) { - // query the json representation of a node and cast the result as resultType - Field formattedField = DSL.field(plpgsqlFunction + "((" + StringUtils.join(tableFields, separator) + ")::" - + sqlType + ")::" + resultType); - - return as(DSL.field(formattedField)); - } - - public Field usingToJson(String sqlType, String separator, Field... tableFields) { - // query the json representation of a node and cast the result as resultType - Field formattedField = DSL.field(toJson( - fieldContext.getContext().configuration(), - DSL.field(StringUtils.join(tableFields, separator)) - .cast(DSL.val(sqlType)) - .toString())); - - return as(DSL.field(formattedField)); - } - - @Override - public Field sqlField() { - return null; - } - - @Override - public IRMObjectAttribute forTableField(TableField tableField) { - return this; - } -} diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/Functions.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/Functions.java index d709b9e9e..ee4c4e546 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/Functions.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/Functions.java @@ -23,6 +23,7 @@ import org.jooq.Function3; import org.jooq.Function4; import org.jooq.TableField; +import org.jooq.impl.DSL; /** * @author Christian Chevalley @@ -67,4 +68,8 @@ private static Field applyFunction3(Function3 function, TableField... tableField private static Field applyFunction4(Function4 function, TableField... tableField) { return (Field) function.apply(tableField[0], tableField[1], tableField[2], tableField[3]); } + + public static Field inline(Field field) { + return DSL.field(field.toString(), field.getType()); + } } diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonField.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonField.java index 3adf12da2..f9c2dcf2c 100644 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonField.java +++ b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonField.java @@ -61,7 +61,7 @@ public class GenericJsonField extends RMObjectAttribute { protected Optional jsonPath = Optional.empty(); - private static final String ITERATIVE_MARKER = "'" + AQL_NODE_ITERATIVE_MARKER + "'"; + private static final String ITERATIVE_MARKER = AQL_NODE_ITERATIVE_MARKER; public GenericJsonField(FieldResolutionContext fieldContext, JoinSetup joinSetup) { super(fieldContext, joinSetup); @@ -139,24 +139,27 @@ public Field jsonField(String rmType, Object function, TableField... tableFields if (tokenized.contains(QueryImplConstants.AQL_NODE_NAME_PREDICATE_MARKER)) { // replace the ITERATIVE_MARKERs by default index - Collections.replaceAll(tokenized, ITERATIVE_MARKER, "'0'"); + Collections.replaceAll(tokenized, ITERATIVE_MARKER, "0"); jsonField = new FunctionBasedNodePredicateCall(fieldContext, tokenized).resolve(function, tableFields); } else if (tokenized.contains(ITERATIVE_MARKER)) jsonField = fieldWithJsonArrayIteration(configuration, tokenized, function, tableFields); else - jsonField = DSL.field(jsonpathItemAsText( + jsonField = jsonpathItemAsText( configuration, - DSL.field(apply(function, tableFields).toString()).cast(JSONB.class), - tokenized.toArray(new String[] {}))); + Functions.inline(apply(function, tableFields).cast(JSONB.class)), + tokenized.toArray(new String[] {})); - } else jsonField = DSL.field(apply(function, tableFields).toString()).cast(String.class); + } else { + // output as sting + jsonField = Functions.inline(apply(function, tableFields)).cast(String.class); + } // check if the SQL expression contains a set returned in a WHERE clause (implying a lateral join) if (jsonField.toString().contains(QueryImplConstants.AQL_NODE_ITERATIVE_FUNCTION) && fieldContext.getClause().equals(IQueryImpl.Clause.WHERE)) - jsonField = DSL.field(DSL.select(jsonField)); + jsonField = DSL.select(jsonField).asField(); - return as(DSL.field(jsonField)); + return as(jsonField); } private Field fieldWithJsonArrayIteration( @@ -169,10 +172,7 @@ private Field fieldWithJsonArrayIteration( .toArray(new String[] {}); // initial - Field field = jsonpathItem( - configuration, - DSL.field(apply(function, tableFields).toString()).cast(JSONB.class), - prefix); + Field field = jsonpathItem(configuration, apply(function, tableFields).cast(JSONB.class), prefix); while (remaining.length > 0) { List tokens = Arrays.asList(remaining.clone()); @@ -185,8 +185,8 @@ private Field fieldWithJsonArrayIteration( remaining = new String[] {}; } - field = DSL.field(jsonpathItemAsText( - configuration, jsonArraySplitElements(configuration, field.cast(JSONB.class)), prefix)); + field = jsonpathItemAsText( + configuration, jsonArraySplitElements(configuration, field.cast(JSONB.class)), prefix); } return field; diff --git a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/SimpleAttribute.java b/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/SimpleAttribute.java deleted file mode 100644 index e60820b7b..000000000 --- a/service/src/main/java/org/ehrbase/aql/sql/queryimpl/value_field/SimpleAttribute.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2019 vitasystems GmbH and Hannover Medical School. - * - * This file is part of project EHRbase - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehrbase.aql.sql.queryimpl.value_field; - -import java.util.Optional; -import org.ehrbase.aql.sql.queryimpl.attribute.FieldResolutionContext; -import org.ehrbase.aql.sql.queryimpl.attribute.IRMObjectAttribute; -import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; -import org.ehrbase.aql.sql.queryimpl.attribute.composition.CompositionAttribute; -import org.jooq.Field; -import org.jooq.TableField; -import org.jooq.impl.DSL; - -@SuppressWarnings({"java:S3740", "java:S1452"}) -public class SimpleAttribute extends CompositionAttribute { - - protected Field tableField; - Optional type = Optional.empty(); - - public SimpleAttribute(FieldResolutionContext fieldContext, JoinSetup joinSetup) { - super(fieldContext, joinSetup); - } - - @Override - public Field sqlField() { - Field actualField; - - if (type.isPresent()) actualField = DSL.field(tableField + "::" + type.get()); - else actualField = DSL.field(tableField); - - return as(actualField); - } - - @Override - public IRMObjectAttribute forTableField(TableField tableField) { - return forTableField(tableField); - } - - public SimpleAttribute forTableField(String pgtype, Field tableField) { - if (pgtype != null) type = Optional.of(pgtype); - - this.tableField = tableField; - return this; - } -} diff --git a/service/src/main/java/org/ehrbase/dao/access/jooq/EntryAccess.java b/service/src/main/java/org/ehrbase/dao/access/jooq/EntryAccess.java index 1db8594a2..6994f473b 100644 --- a/service/src/main/java/org/ehrbase/dao/access/jooq/EntryAccess.java +++ b/service/src/main/java/org/ehrbase/dao/access/jooq/EntryAccess.java @@ -477,16 +477,16 @@ public Boolean update(Timestamp transactionTime, boolean force) { UpdateQuery updateQuery = getContext().updateQuery(ENTRY); updateQuery.addValue(ENTRY.COMPOSITION_ID, getCompositionId()); - updateQuery.addValue(ENTRY.SEQUENCE, DSL.field(DSL.val(getSequence()))); - updateQuery.addValue(ENTRY.TEMPLATE_ID, DSL.field(DSL.val(getTemplateId()))); - - updateQuery.addValue(ENTRY.ITEM_TYPE, DSL.field(DSL.val(EntryType.valueOf(getItemType())))); - updateQuery.addValue(ENTRY.ARCHETYPE_ID, DSL.field(DSL.val(getArchetypeId()))); - updateQuery.addValue(ENTRY.CATEGORY, DSL.field(DSL.val(getCategory()))); - updateQuery.addValue(ENTRY.ENTRY_, DSL.field(DSL.val(getEntryJson()))); - updateQuery.addValue(ENTRY.SYS_TRANSACTION, DSL.field(DSL.val(transactionTime))); - updateQuery.addValue(ENTRY.NAME, DSL.field(DSL.val(getCompositionName()))); - updateQuery.addValue(ENTRY.RM_VERSION, DSL.field(DSL.val(getRmVersion()))); + updateQuery.addValue(ENTRY.SEQUENCE, DSL.val(getSequence())); + updateQuery.addValue(ENTRY.TEMPLATE_ID, DSL.val(getTemplateId())); + + updateQuery.addValue(ENTRY.ITEM_TYPE, DSL.val(EntryType.valueOf(getItemType()))); + updateQuery.addValue(ENTRY.ARCHETYPE_ID, DSL.val(getArchetypeId())); + updateQuery.addValue(ENTRY.CATEGORY, DSL.val(getCategory())); + updateQuery.addValue(ENTRY.ENTRY_, DSL.val(getEntryJson())); + updateQuery.addValue(ENTRY.SYS_TRANSACTION, DSL.val(transactionTime)); + updateQuery.addValue(ENTRY.NAME, DSL.val(getCompositionName())); + updateQuery.addValue(ENTRY.RM_VERSION, DSL.val(getRmVersion())); updateQuery.addConditions(ENTRY.ID.eq(getId())); logger.debug("Update done..."); diff --git a/service/src/main/java/org/ehrbase/dao/access/jooq/party/PersistedPartyRelated.java b/service/src/main/java/org/ehrbase/dao/access/jooq/party/PersistedPartyRelated.java index 0dc9dcef3..c2b02f2ac 100644 --- a/service/src/main/java/org/ehrbase/dao/access/jooq/party/PersistedPartyRelated.java +++ b/service/src/main/java/org/ehrbase/dao/access/jooq/party/PersistedPartyRelated.java @@ -146,7 +146,10 @@ public UUID findInDB(PartyProxy partyProxy) { .and(PARTY_IDENTIFIED.PARTY_REF_SCHEME.isNull()) .and(PARTY_IDENTIFIED.PARTY_REF_TYPE.isNull()) .and(PARTY_IDENTIFIED.NAME.eq(((PartyIdentified) partyProxy).getName())) - .and(DSL.field("(" + PARTY_IDENTIFIED.RELATIONSHIP + ").value") + .and(DSL.field( + "({0}).{1}", + PARTY_IDENTIFIED.RELATIONSHIP, + org.ehrbase.jooq.pg.udt.DvCodedText.VALUE.getUnqualifiedName()) .eq(relationshipAsRecord(partyProxy) .getValue())) .and(PARTY_IDENTIFIED.PARTY_TYPE.eq(PartyType.party_related))); diff --git a/service/src/main/java/org/ehrbase/repository/EhrFolderRepository.java b/service/src/main/java/org/ehrbase/repository/EhrFolderRepository.java index 18de6a4fb..f1909cedc 100644 --- a/service/src/main/java/org/ehrbase/repository/EhrFolderRepository.java +++ b/service/src/main/java/org/ehrbase/repository/EhrFolderRepository.java @@ -380,8 +380,8 @@ public Field[] convertToEhrFolderHistoryFields(Field[] fields) { private static SelectJoinStep headQuery(DSLContext context) { return context.select(EHR_FOLDER.fields()) .select( - DSL.field("null").as(EHR_FOLDER_HISTORY.SYS_PERIOD_UPPER.getName()), - DSL.field("false").as(EHR_FOLDER_HISTORY.SYS_DELETED.getName())) + DSL.inline((OffsetDateTime) null).as(EHR_FOLDER_HISTORY.SYS_PERIOD_UPPER.getName()), + DSL.inline(false).as(EHR_FOLDER_HISTORY.SYS_DELETED.getName())) .from(EHR_FOLDER); } diff --git a/service/src/main/java/org/ehrbase/repository/PartyProxyRepository.java b/service/src/main/java/org/ehrbase/repository/PartyProxyRepository.java index 004459484..77b75da7a 100644 --- a/service/src/main/java/org/ehrbase/repository/PartyProxyRepository.java +++ b/service/src/main/java/org/ehrbase/repository/PartyProxyRepository.java @@ -256,7 +256,10 @@ private Optional findInDBPartyRelated(PartyRelated partyProxy) { .and(PARTY_IDENTIFIED.PARTY_REF_SCHEME.isNull()) .and(PARTY_IDENTIFIED.PARTY_REF_TYPE.isNull()) .and(PARTY_IDENTIFIED.NAME.eq(partyProxy.getName())) - .and(DSL.field("(" + PARTY_IDENTIFIED.RELATIONSHIP + ").value") + .and(DSL.field( + "({0}).{1}", + PARTY_IDENTIFIED.RELATIONSHIP, + org.ehrbase.jooq.pg.udt.DvCodedText.VALUE.getUnqualifiedName()) .eq(relationshipAsRecord(partyProxy).getValue())) .and(PARTY_IDENTIFIED.PARTY_TYPE.eq(PartyType.party_related))); diff --git a/service/src/main/java/org/ehrbase/service/PersistenceConfig.java b/service/src/main/java/org/ehrbase/service/PersistenceConfig.java index 905845697..93144d492 100644 --- a/service/src/main/java/org/ehrbase/service/PersistenceConfig.java +++ b/service/src/main/java/org/ehrbase/service/PersistenceConfig.java @@ -78,10 +78,12 @@ public Connection acquire() { return connection; } catch (SQLException e) { try { + super.release(connection); - } finally { - throw new DataAccessException("Failed to set default tenant", e); + } catch (Exception r) { + } + throw new DataAccessException("Failed to set default tenant", e); } } }; diff --git a/service/src/main/java/org/ehrbase/service/TemplateServiceImp.java b/service/src/main/java/org/ehrbase/service/TemplateServiceImp.java index e86725a40..1f433cda3 100644 --- a/service/src/main/java/org/ehrbase/service/TemplateServiceImp.java +++ b/service/src/main/java/org/ehrbase/service/TemplateServiceImp.java @@ -134,7 +134,7 @@ public Composition buildExample(String templateId) { public WebTemplate findTemplate(String templateId) { try { return knowledgeCacheService.getQueryOptMetaData(templateId); - } catch (NullPointerException e) { + } catch (NullPointerException | IllegalArgumentException e) { throw new ObjectNotFoundException("template", "Template with the specified id does not exist", e); } catch (Exception e) { throw new InternalServerException("Could not generate web template", e); diff --git a/service/src/main/java/org/ehrbase/tenant/extraction/AuthenticatedExtractionStrategy.java b/service/src/main/java/org/ehrbase/tenant/extraction/AuthenticatedExtractionStrategy.java index b481c6692..097915607 100644 --- a/service/src/main/java/org/ehrbase/tenant/extraction/AuthenticatedExtractionStrategy.java +++ b/service/src/main/java/org/ehrbase/tenant/extraction/AuthenticatedExtractionStrategy.java @@ -153,7 +153,7 @@ public boolean accept(Object... args) { } public Optional> extract(Object... args) { - return extract(null, args); + return extractWithPrior(Optional.empty(), args); } } // @format:on diff --git a/service/src/test/java/org/ehrbase/aql/sql/binding/OrderByBinderTest.java b/service/src/test/java/org/ehrbase/aql/sql/binding/OrderByBinderTest.java index 5fc9e4e60..65bb54364 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/binding/OrderByBinderTest.java +++ b/service/src/test/java/org/ehrbase/aql/sql/binding/OrderByBinderTest.java @@ -24,6 +24,9 @@ import org.ehrbase.aql.compiler.OrderAttribute; import org.ehrbase.aql.definition.I_VariableDefinitionHelper; import org.ehrbase.dao.jooq.impl.DSLContextHelper; +import org.ehrbase.jooq.pg.tables.Ehr; +import org.jooq.Record; +import org.jooq.SelectQuery; import org.jooq.SortField; import org.junit.Test; @@ -33,17 +36,17 @@ public class OrderByBinderTest { public void getOrderByFields() { // ascending + SelectQuery query = DSLContextHelper.buildContext().selectQuery(); + query.addSelect(Ehr.EHR_.DATE_CREATED.as("date_created")); + query.addFrom(Ehr.EHR_); { // represents a/context/start_time/value as date_created and order by date_created ASC OrderAttribute orderAttribute = new OrderAttribute( I_VariableDefinitionHelper.build(null, "date_created", null, false, false, false)); orderAttribute.setDirection(OrderAttribute.OrderDirection.ASC); - OrderByBinder cut = new OrderByBinder( - null, - Collections.singletonList(orderAttribute), - DSLContextHelper.buildContext().selectQuery()); - List> actual = cut.getOrderByFields(); + OrderByBinder cut = new OrderByBinder(null, Collections.singletonList(orderAttribute), query); + List> actual = cut.getOrderByFields(); assertThat(actual).size().isEqualTo(1); SortField sortField = actual.get(0); @@ -57,11 +60,8 @@ public void getOrderByFields() { I_VariableDefinitionHelper.build(null, "date_created", null, false, false, false)); orderAttribute.setDirection(OrderAttribute.OrderDirection.DESC); - OrderByBinder cut = new OrderByBinder( - null, - Collections.singletonList(orderAttribute), - DSLContextHelper.buildContext().selectQuery()); - List> actual = cut.getOrderByFields(); + OrderByBinder cut = new OrderByBinder(null, Collections.singletonList(orderAttribute), query); + List> actual = cut.getOrderByFields(); assertThat(actual).size().isEqualTo(1); SortField sortField = actual.get(0); @@ -74,11 +74,8 @@ public void getOrderByFields() { OrderAttribute orderAttribute = new OrderAttribute( I_VariableDefinitionHelper.build(null, "date_created", null, false, false, false)); - OrderByBinder cut = new OrderByBinder( - null, - Collections.singletonList(orderAttribute), - DSLContextHelper.buildContext().selectQuery()); - List> actual = cut.getOrderByFields(); + OrderByBinder cut = new OrderByBinder(null, Collections.singletonList(orderAttribute), query); + List> actual = cut.getOrderByFields(); assertThat(actual).size().isEqualTo(1); SortField sortField = actual.get(0); diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCallTest.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCallTest.java index d862fce8b..e244f58c4 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCallTest.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/FunctionBasedNodePredicateCallTest.java @@ -17,10 +17,10 @@ */ package org.ehrbase.aql.sql.queryimpl; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.ehrbase.aql.sql.queryimpl.QueryImplConstants.AQL_NODE_NAME_PREDICATE_MARKER; import static org.ehrbase.jooq.pg.Tables.EVENT_CONTEXT; import static org.jooq.impl.DSL.select; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.Arrays; @@ -105,16 +105,16 @@ public void testConstruct2() { @Test public void testConstruct3() { String[] pathArray = { - "'other_context'", - "'/items[openEHR-EHR-CLUSTER.case_identification.v0]'", + "other_context", + "/items[openEHR-EHR-CLUSTER.case_identification.v0]", AQL_NODE_NAME_PREDICATE_MARKER, - "'item-1'", - "'/items[at0001]'", + "item-1", + "/items[at0001]", AQL_NODE_NAME_PREDICATE_MARKER, - "'item-2'", - "'/name'", - "'0'", - "'value'", + "item-2", + "/name", + "0", + "value", }; FunctionBasedNodePredicateCall functionBasedNodePredicateCall = @@ -124,17 +124,17 @@ public void testConstruct3() { String dummySelect = select(test).toString(); - assertEquals( - dummySelect, - "select jsonb_extract_path_text(cast(\"ehr\".\"aql_node_name_predicate\"(\n" - + " cast(jsonb_extract_path_text(\"ehr\".\"aql_node_name_predicate\"(\n" - + " cast(jsonb_extract_path_text(cast(\"ehr\".\"js_context\"(\"ehr\".\"event_context\".\"id\") as jsonb),'other_context','/items[openEHR-EHR-CLUSTER.case_identification.v0]') as jsonb),\n" - + " 'item-1',\n" - + " ''\n" - + "),'/items[at0001]') as jsonb),\n" - + " 'item-2',\n" - + " ''\n" - + ") as jsonb),'/name','0','value')"); + assertThat(dummySelect) + .isEqualToIgnoringWhitespace( + "select jsonb_extract_path_text(cast(\"ehr\".\"aql_node_name_predicate\"(\n" + + " cast(jsonb_extract_path_text(\"ehr\".\"aql_node_name_predicate\"(\n" + + " cast(jsonb_extract_path_text(cast(\"ehr\".\"js_context\"(\"ehr\".\"event_context\".\"id\") as jsonb),'other_context','/items[openEHR-EHR-CLUSTER.case_identification.v0]') as jsonb),\n" + + " 'item-1',\n" + + " ''\n" + + "),'/items[at0001]') as jsonb),\n" + + " 'item-2',\n" + + " ''\n" + + ") as jsonb),'/name','0','value')"); } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZoneTest.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZoneTest.java index 164f985c7..320dd90f3 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZoneTest.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/attribute/TemporalWithTimeZoneTest.java @@ -58,7 +58,7 @@ public void testJsonPathBuilding() { .as("test formatting dvdatetime value") .isEqualToIgnoringWhitespace("select jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\n" + " \"ehr\".\"event_context\".\"start_time\", \n" - + " event_context.START_TIME_TZID\n" + + " \"ehr\".\"event_context\".\"start_time_tzid\"\n" + ") as jsonb),'value') \"/test\""); } } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/UC40.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/UC40.java index adc59d480..c3b7d9c5a 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/UC40.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/UC40.java @@ -22,10 +22,11 @@ public abstract class UC40 extends QueryProcessorTestBase { protected UC40() { - this.aql = "select" + " CAST (d/description[at0001]/items[at0004]/value/magnitude AS 'FLOAT') as max_magnitude" - + " from EHR e" - + " contains COMPOSITION" - + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]"; + this.aql = + "select" + " CAST (d/description[at0001]/items[at0004]/value/magnitude AS 'NUMERIC') as max_magnitude" + + " from EHR e" + + " contains COMPOSITION" + + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]"; this.expectedOutputWithJson = false; } } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC15Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC15Test.java index 8341f78f1..102ce98e9 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC15Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC15Test.java @@ -29,7 +29,7 @@ public UC15Test() { super(); this.expectedSqlExpression = "select distinct on (\"/ehr_status/other_details\") \"\".\"/ehr_status/other_details\"" - + " from (select jsonb_extract_path_text(cast(\"ehr\".\"js_ehr_status\"(\"status_join\".\"ehr_id\") as jsonb),'other_details') as \"/ehr_status/other_details\"" + + " from (select jsonb_extract_path_text(cast(\"ehr\".\"js_ehr_status\"(\"status_join\".\"ehr_id\") as jsonb), 'other_details') as \"/ehr_status/other_details\"" + " from \"ehr\".\"ehr\" as \"ehr_join\"" + " join \"ehr\".\"status\" as \"status_join\"" + " on \"status_join\".\"ehr_id\" = \"ehr_join\".\"id\"" diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC20Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC20Test.java index 51e9e673c..5c560c409 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC20Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC20Test.java @@ -27,7 +27,7 @@ public class UC20Test extends UC20 { public UC20Test() { super(); this.expectedSqlExpression = - "select jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb),'defining_code') as \"/category/defining_code\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" right outer join \"ehr\".\"ehr\" as \"ehr_join\" on \"ehr_join\".\"id\" = \"composition_join\".\"ehr_id\" where (\"ehr\".\"entry\".\"template_id\" = ? and (\"ehr_join\".\"id\" = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'))"; + "select jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb), 'defining_code') as \"/category/defining_code\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" right outer join \"ehr\".\"ehr\" as \"ehr_join\" on \"ehr_join\".\"id\" = \"composition_join\".\"ehr_id\" where (\"ehr\".\"entry\".\"template_id\" = ? and (\"ehr_join\".\"id\" = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'))"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC21Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC21Test.java index 61653eb5c..60b541545 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC21Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC21Test.java @@ -27,7 +27,7 @@ public class UC21Test extends UC21 { public UC21Test() { super(); this.expectedSqlExpression = - "select jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb),'defining_code','terminology_id','value') as \"/category/defining_code/terminology_id/value\"" + "select jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb),'defining_code', 'terminology_id', 'value') as \"/category/defining_code/terminology_id/value\"" + " from \"ehr\".\"entry\"" + " right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\"" + " right outer join \"ehr\".\"ehr\" as \"ehr_join\" on \"ehr_join\".\"id\" = \"composition_join\".\"ehr_id\"" diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC23Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC23Test.java index 5779fc62d..740d42f78 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC23Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC23Test.java @@ -27,7 +27,7 @@ public class UC23Test extends UC23 { public UC23Test() { super(); this.expectedSqlExpression = - "select count(DISTINCT \"_FCT_ARG_0\") as \"count\" from (select cast(\"ehr\".\"js_dv_date_time\"(\n" + "select count(distinct \"_FCT_ARG_0\") as \"count\" from (select cast(\"ehr\".\"js_dv_date_time\"(\n" + " \"ehr_join\".\"date_created\", \n" + " \"ehr_join\".\"date_created_tzid\"\n" + ") as varchar) as \"_FCT_ARG_0\" from \"ehr\".\"ehr\" as \"ehr_join\") as \"\""; diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC24Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC24Test.java index eb72d28fe..1fe522040 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC24Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC24Test.java @@ -27,7 +27,7 @@ public class UC24Test extends UC24 { public UC24Test() { super(); this.expectedSqlExpression = - "select cast(\"ehr\".\"js_composition\"(cast(cast(composition_join.id as uuid) as uuid), cast(? as text)) as varchar) as \"c\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" where (\"ehr\".\"entry\".\"template_id\" = ? and ( null IS NULL ))"; + "select cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as varchar) as \"c\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" where (\"ehr\".\"entry\".\"template_id\" = ? and ( null IS NULL ))"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC27Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC27Test.java index 9fce1948c..edaa97611 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC27Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC27Test.java @@ -28,10 +28,10 @@ public UC27Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid),\n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\",\n" + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where (\"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c' and 'case1'IN ( (ARRAY.COLUMN)::TEXT ) )"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC28Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC28Test.java index 045d13950..53078acbc 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC28Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC28Test.java @@ -28,10 +28,10 @@ public UC28Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid),\n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\",\n" + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where (\"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c' and 'case1' = SOME ( (SELECT (ARRAY.COLUMN)::TEXT ) ) )"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC29Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC29Test.java index e9b059316..3279bf119 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC29Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC29Test.java @@ -28,10 +28,10 @@ public UC29Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid),\n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\" ,\n" + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where (\"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c' and 'case1' = ANY ( (SELECT (ARRAY.COLUMN)::TEXT ) ) )"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC30Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC30Test.java index 43f6cbe83..0ea51e629 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC30Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC30Test.java @@ -28,10 +28,10 @@ public UC30Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid),\n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\",\n" + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where (\"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c' and 'case1' = ALL ( (SELECT (ARRAY.COLUMN)::TEXT ) ) )"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC31Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC31Test.java index cb072e55c..d7368d944 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC31Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC31Test.java @@ -28,10 +28,10 @@ public UC31Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid),\n" - + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path( cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\",\n" + + " 'local'\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where ('case1' = ALL ( (SELECT (ARRAY.COLUMN)::TEXT ) ) and \"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c')"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC32Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC32Test.java index 6117a513c..a3a96c2d4 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC32Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC32Test.java @@ -30,7 +30,7 @@ public class UC32Test extends UC32 { public UC32Test() { super(); this.expectedSqlExpression = "select " + QueryImplConstants.AQL_NODE_ITERATIVE_FUNCTION - + "(ehr.js_ehr(ehr_join.id,'local')::jsonb #>'{folders}') #>>'{name,value}' as \"/folders/name/value\"" + + "(ehr.js_ehr(ehr_join.id,cast(? as text))::jsonb #>'{folders}') #>>'{name,value}' as \"/folders/name/value\"" + " from \"ehr\".\"ehr\" as \"ehr_join\"" + " where (" + " 'case1' IN (SELECT regexp_split_to_array('case1,case2', ','))" diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC33Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC33Test.java index 2666a217a..1e561f0db 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC33Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC33Test.java @@ -28,10 +28,10 @@ public UC33Test() { super(); this.expectedSqlExpression = "select (ARRAY.COLUMN)::TEXT as \"/folders/name/value\" from \"ehr\".\"ehr\" as \"ehr_join\" join lateral (\n" - + " select jsonb_extract_path_text(cast(ehr.xjsonb_array_elements(cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" - + " cast(ehr_join.id as uuid), \n" + + " select jsonb_extract_path_text(ehr.xjsonb_array_elements(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + + " \"ehr_join\".\"id\", \n" + " 'local'\n" - + ") as jsonb),'folders') as jsonb)) as jsonb),'name','0','value')\n" + + ") as jsonb),'folders')),'name','0','value')\n" + " AS COLUMN) as \"ARRAY\" on true where ('case1'IN ( 'case1','case2' ) and \"ehr_join\".\"id\" = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c')"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC34Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC34Test.java index 818d9f51e..35905abc1 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC34Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC34Test.java @@ -27,7 +27,7 @@ public class UC34Test extends UC34 { public UC34Test() { super(); this.expectedSqlExpression = - "select min(DISTINCT \"_FCT_ARG_0\") as \"min\" from (select cast(\"ehr\".\"js_dv_date_time\"(\n" + "select min(distinct \"_FCT_ARG_0\") as \"min\" from (select cast(\"ehr\".\"js_dv_date_time\"(\n" + " \"ehr_join\".\"date_created\", \n" + " \"ehr_join\".\"date_created_tzid\"\n" + ") as varchar) as \"_FCT_ARG_0\" from \"ehr\".\"ehr\" as \"ehr_join\") as \"\""; diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC35Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC35Test.java index cb3332acc..91387f261 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC35Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC35Test.java @@ -27,9 +27,9 @@ public class UC35Test extends UC35 { public UC35Test() { super(); this.expectedSqlExpression = - "select cast(jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + " cast(ehr_join.id as uuid), \n" + "select jsonb_extract_path(cast(\"ehr\".\"js_ehr\"(\n" + " \"ehr_join\".\"id\" , \n" + " 'local'\n" - + ") as jsonb),'directory') as jsonb) as \"/directory\" from \"ehr\".\"ehr\" as \"ehr_join\""; + + ") as jsonb),'directory') as \"/directory\" from \"ehr\".\"ehr\" as \"ehr_join\""; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC36Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC36Test.java index 0e4a88492..91dc1e1c8 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC36Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC36Test.java @@ -26,8 +26,8 @@ public class UC36Test extends UC36 { public UC36Test() { super(); - this.expectedSqlExpression = "select cast(\"ehr\".\"js_ehr\"(\n" + " cast(ehr_join.id as uuid), \n" - + " ?\n" + this.expectedSqlExpression = "select cast(\"ehr\".\"js_ehr\"(\n" + " \"ehr_join\".\"id\", \n" + + " 'local'\n" + ") as varchar) as \"e\"\n" + "from \"ehr\".\"ehr\" as \"ehr_join\""; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC40Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC40Test.java index 1d3d7f292..211c55678 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC40Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC40Test.java @@ -27,7 +27,7 @@ public class UC40Test extends UC40 { public UC40Test() { super(); this.expectedSqlExpression = - "select CAST (\"max_magnitude\" AS FLOAT ) as \"max_magnitude\" from (select (ARRAY.COLUMN#>>'{/description[at0001],/items[at0004],0,/value,magnitude}')::bigint as \"max_magnitude\" from \"ehr\".\"entry\" join lateral (\n" + "select cast(\"max_magnitude\" as numeric) as \"max_magnitude\" from (select (ARRAY.COLUMN#>>'{/description[at0001],/items[at0004],0,/value,magnitude}')::bigint as \"max_magnitude\" from \"ehr\".\"entry\" join lateral (\n" + " select (ehr.xjsonb_array_elements((\"ehr\".\"entry\".\"entry\"#>>'{/composition[openEHR-EHR-COMPOSITION.health_summary.v1],/content[openEHR-EHR-ACTION.immunisation_procedure.v1]}')::jsonb)) \n" + " AS COLUMN) as \"ARRAY\" on true where \"ehr\".\"entry\".\"template_id\" = ?) as \"\""; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC41Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC41Test.java index 4f39292a0..c540584e5 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC41Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC41Test.java @@ -26,7 +26,7 @@ public class UC41Test extends UC41 { public UC41Test() { super(); - this.expectedSqlExpression = "select ? as \"constant\" from \"ehr\".\"entry\""; + this.expectedSqlExpression = "select true as \"constant\" from \"ehr\".\"entry\""; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC42Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC42Test.java index a4ac072a9..bcedf0d19 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC42Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC42Test.java @@ -27,41 +27,7 @@ public class UC42Test extends UC42 { public UC42Test() { super(); this.expectedSqlExpression = - "select ARRAY.COLUMN as \"Diagnose\", ARRAY.COLUMN as \"MabuseComposition\", ARRAY.COLUMN as \"NewerComposition\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"event_context\" on \"ehr\".\"event_context\".\"composition_id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"party_identified\" as \"composer_ref\" on \"composition_join\".\"composer\" = \"composer_ref\".\"id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"Diagnose\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Diagnose' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"MabuseComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_party_ref\"(\n" - + " \"composer_ref\".\"party_ref_value\", \n" - + " \"composer_ref\".\"party_ref_scheme\", \n" - + " \"composer_ref\".\"party_ref_namespace\", \n" - + " \"composer_ref\".\"party_ref_type\"\n" - + ") as jsonb),'id','value') as varchar) = cast('Dr Mabuse' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"NewerComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\n" - + " \"ehr\".\"event_context\".\"start_time\", \n" - + " event_context.START_TIME_TZID\n" - + ") as jsonb),'value') as varchar) > cast('2020-01-01' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ? union all select ARRAY.COLUMN as \"Diagnose\", ARRAY.COLUMN as \"MabuseComposition\", ARRAY.COLUMN as \"NewerComposition\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"event_context\" on \"ehr\".\"event_context\".\"composition_id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"party_identified\" as \"composer_ref\" on \"composition_join\".\"composer\" = \"composer_ref\".\"id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"Diagnose\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Diagnose' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"MabuseComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_party_ref\"(\n" - + " \"composer_ref\".\"party_ref_value\", \n" - + " \"composer_ref\".\"party_ref_scheme\", \n" - + " \"composer_ref\".\"party_ref_namespace\", \n" - + " \"composer_ref\".\"party_ref_type\"\n" - + ") as jsonb),'id','value') as varchar) = cast('Dr Mabuse' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"NewerComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\n" - + " \"ehr\".\"event_context\".\"start_time\", \n" - + " event_context.START_TIME_TZID\n" - + ") as jsonb),'value') as varchar) > cast('2020-01-01' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ?"; + "select ARRAY.COLUMN as \"Diagnose\", ARRAY.COLUMN as \"MabuseComposition\", ARRAY.COLUMN as \"NewerComposition\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"event_context\" on \"ehr\".\"event_context\".\"composition_id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"party_identified\" as \"composer_ref\" on \"composition_join\".\"composer\" = \"composer_ref\".\"id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"Diagnose\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb), 'value') as varchar) = cast('Diagnose' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"MabuseComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_party_ref\"(\"composer_ref\".\"party_ref_value\", \"composer_ref\".\"party_ref_scheme\", \"composer_ref\".\"party_ref_namespace\", \"composer_ref\".\"party_ref_type\") as jsonb), 'id', 'value') as varchar) = cast('Dr Mabuse' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"NewerComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\"ehr\".\"event_context\".\"start_time\", \"ehr\".\"event_context\".\"start_time_tzid\") as jsonb), 'value') as varchar) > cast('2020-01-01' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ? union all select ARRAY.COLUMN as \"Diagnose\", ARRAY.COLUMN as \"MabuseComposition\", ARRAY.COLUMN as \"NewerComposition\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"event_context\" on \"ehr\".\"event_context\".\"composition_id\" = \"ehr\".\"entry\".\"composition_id\" join \"ehr\".\"party_identified\" as \"composer_ref\" on \"composition_join\".\"composer\" = \"composer_ref\".\"id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"Diagnose\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb), 'value') as varchar) = cast('Diagnose' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"MabuseComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_party_ref\"(\"composer_ref\".\"party_ref_value\", \"composer_ref\".\"party_ref_scheme\", \"composer_ref\".\"party_ref_namespace\", \"composer_ref\".\"party_ref_type\") as jsonb), 'id', 'value') as varchar) = cast('Dr Mabuse' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as jsonb), 'uid', 'value') as \"NewerComposition\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\"ehr\".\"event_context\".\"start_time\", \"ehr\".\"event_context\".\"start_time_tzid\") as jsonb), 'value') as varchar) > cast('2020-01-01' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ?"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC43Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC43Test.java index 8b0001d79..0190f5b19 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC43Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC43Test.java @@ -27,19 +27,19 @@ public class UC43Test extends UC43 { public UC43Test() { super(); this.expectedSqlExpression = - "select ARRAY.COLUMN as \"uid1\", ARRAY.COLUMN as \"uid2\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"uid1\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-1' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"uid2\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-2' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ? union all select ARRAY.COLUMN as \"uid1\", ARRAY.COLUMN as \"uid2\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"uid1\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-1' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(\n" - + " cast(composition_join.id as uuid), \n" - + " 'local'\n" - + ") as jsonb),'uid','value') as \"uid2\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-2' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ?"; + "select ARRAY.COLUMN as \"uid1\", ARRAY.COLUMN as \"uid2\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(" + + " cast(\"composition_join\".\"id\" as uuid), " + + " cast('local' as text)" + + ") as jsonb),'uid', 'value') as \"uid1\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-1' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(" + + " cast(\"composition_join\".\"id\" as uuid), " + + " cast('local' as text)" + + ") as jsonb),'uid', 'value') as \"uid2\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-2' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ? union all select ARRAY.COLUMN as \"uid1\", ARRAY.COLUMN as \"uid2\" from \"ehr\".\"entry\" right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\" left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(" + + " cast(\"composition_join\".\"id\" as uuid), " + + " cast('local' as text)" + + ") as jsonb),'uid', 'value') as \"uid1\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-1' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? left outer join lateral (select (select jsonb_extract_path_text(cast(\"ehr\".\"js_composition\"(" + + " cast(\"composition_join\".\"id\" as uuid), " + + " cast('local' as text)" + + ") as jsonb),'uid', 'value') as \"uid2\" where cast(jsonb_extract_path_text(cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"name\") as jsonb),'value') as varchar) = cast('Laborbefund-2' as varchar)) as \"COLUMN\") as \"ARRAY\" on ? where \"ehr\".\"entry\".\"template_id\" = ?"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC46Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC46Test.java index f0be926f2..fc88bf0c9 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC46Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC46Test.java @@ -29,7 +29,7 @@ public UC46Test() { this.expectedSqlExpression = "select distinct on (\"description\", \"timing\") \"\".\"description\", \"\".\"timing\" from (select (ARRAY.COLUMN#>>'{/description[at0001],/items[at0002],0,/value,value}')::TEXT as \"description\", (ARRAY.COLUMN#>>'{/time,/value}')::TEXT as \"timing\" from \"ehr\".\"entry\" join lateral (\n" + " select (ehr.xjsonb_array_elements((\"ehr\".\"entry\".\"entry\"#>>'{/composition[openEHR-EHR-COMPOSITION.health_summary.v1],/content[openEHR-EHR-ACTION.immunisation_procedure.v1]}')::jsonb)) \n" - + " AS COLUMN) as \"ARRAY\" on true where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"description\" asc"; + + " AS COLUMN) as \"ARRAY\" on true where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"\".\"description\" asc"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC4Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC4Test.java index 1c73d75d5..1ca4404da 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC4Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC4Test.java @@ -29,13 +29,13 @@ public UC4Test() { this.expectedSqlExpression = "select \"\".\"/composer/name\", \"\".\"/context/start_time/value\" from (select \"composer_ref\".\"name\" as \"/composer/name\", jsonb_extract_path_text(cast(\"ehr\".\"js_dv_date_time\"(\n" + " \"ehr\".\"event_context\".\"start_time\",\n" - + " event_context.START_TIME_TZID\n" + + " \"ehr\".\"event_context\".\"start_time_tzid\"\n" + ") as jsonb),'value') as \"/context/start_time/value\"" + " from \"ehr\".\"entry\"" + " right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\"" + " join \"ehr\".\"event_context\" on \"ehr\".\"event_context\".\"composition_id\" = \"ehr\".\"entry\".\"composition_id\"" + " join \"ehr\".\"party_identified\" as \"composer_ref\" on \"composition_join\".\"composer\" = \"composer_ref\".\"id\"" - + " where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"/context/start_time/value\" desc"; + + " where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"\".\"/context/start_time/value\" desc"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC6Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC6Test.java index 885522970..9883d25bc 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC6Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC6Test.java @@ -29,7 +29,7 @@ public UC6Test() { this.expectedSqlExpression = "select \"\".\"description\" from (select (ARRAY.COLUMN#>>'{/description[at0001],/items[at0002],0,/value,value}')::TEXT as \"description\" from \"ehr\".\"entry\" join lateral (\n" + " select (ehr.xjsonb_array_elements((\"ehr\".\"entry\".\"entry\"#>>'{/composition[openEHR-EHR-COMPOSITION.health_summary.v1],/content[openEHR-EHR-ACTION.immunisation_procedure.v1]}')::jsonb)) \n" - + " AS COLUMN) as \"ARRAY\" on true where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"description\" asc"; + + " AS COLUMN) as \"ARRAY\" on true where \"ehr\".\"entry\".\"template_id\" = ?) as \"\" order by \"\".\"description\" asc"; } @Test diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC8Test.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC8Test.java index fa3476442..50fbedf5f 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC8Test.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/translator/testcase/pg10/pgsql/UC8Test.java @@ -27,7 +27,7 @@ public class UC8Test extends UC8 { public UC8Test() { super(); this.expectedSqlExpression = - "select cast(\"ehr\".\"js_composition\"(cast(cast(composition_join.id as uuid) as uuid), cast(? as text)) as varchar) as \"c\" from \"ehr\".\"entry\"" + "select cast(\"ehr\".\"js_composition\"(cast(\"composition_join\".\"id\" as uuid), cast('local' as text)) as varchar) as \"c\" from \"ehr\".\"entry\"" + " right outer join \"ehr\".\"composition\" as \"composition_join\" on \"composition_join\".\"id\" = \"ehr\".\"entry\".\"composition_id\"" + " where \"ehr\".\"entry\".\"template_id\" = ?"; } diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedFieldTest.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedFieldTest.java deleted file mode 100644 index 79e455fe6..000000000 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/FormattedFieldTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020 vitasystems GmbH and Hannover Medical School. - * - * This file is part of project EHRbase - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.ehrbase.aql.sql.queryimpl.value_field; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.ehrbase.jooq.pg.Tables.EHR_; -import static org.junit.Assert.*; - -import org.ehrbase.aql.TestAqlBase; -import org.ehrbase.aql.definition.VariableDefinition; -import org.ehrbase.aql.sql.binding.JoinBinder; -import org.ehrbase.aql.sql.queryimpl.IQueryImpl; -import org.ehrbase.aql.sql.queryimpl.attribute.FieldResolutionContext; -import org.ehrbase.aql.sql.queryimpl.attribute.JoinSetup; -import org.jooq.Field; -import org.jooq.impl.DSL; -import org.junit.Before; -import org.junit.Test; - -public class FormattedFieldTest extends TestAqlBase { - - FieldResolutionContext fieldResolutionContext; - JoinSetup joinSetup = new JoinSetup(); - - @Before - public void setUp() { - fieldResolutionContext = new FieldResolutionContext( - testDomainAccess.getContext(), - "test", - "test", - new VariableDefinition("test", null, "test", false), - IQueryImpl.Clause.SELECT, - null, - testDomainAccess.getIntrospectService(), - null); - } - - @Test - public void testSelectEhrDateCreatedValue() { - Field field = new FormattedField(fieldResolutionContext, joinSetup) - .usingToJson( - "timestamp with time zone", - "||", - JoinBinder.ehrRecordTable.field(EHR_.DATE_CREATED), - JoinBinder.ehrRecordTable.field(EHR_.DATE_CREATED_TZID)); - - assertNotNull(field); - assertThat(DSL.select(field).getQuery().toString()) - .as("test formatting dvdatetime value") - .isEqualToIgnoringWhitespace( - "select cast(to_json(cast(\"ehr_join\".\"date_created\"||\"ehr_join\".\"date_created_tzid\" as varchar)) as jsonb) \"/test\""); - } -} diff --git a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonFieldTest.java b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonFieldTest.java index d6ceec4ac..ddf7a54f6 100644 --- a/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonFieldTest.java +++ b/service/src/test/java/org/ehrbase/aql/sql/queryimpl/value_field/GenericJsonFieldTest.java @@ -91,15 +91,11 @@ public void testFieldWithIterativeMarker() { assertThat(DSL.select(field).getQuery().toString()) .as(jsonPath) - .isEqualToIgnoringWhitespace("select" + " jsonb_extract_path_text(" - + " cast(" - + QueryImplConstants.AQL_NODE_ITERATIVE_FUNCTION + "(" - + " cast(cast(jsonb_extract_path(" - + " cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb),'mappings')" - + " as jsonb)" - + " as jsonb))" - + " as jsonb)," - + " 'match') \"/test\""); + .isEqualToIgnoringWhitespace( + "select" + " jsonb_extract_path_text(" + + QueryImplConstants.AQL_NODE_ITERATIVE_FUNCTION + "(" + + " cast( jsonb_extract_path(" + + " cast(\"ehr\".\"js_dv_coded_text_inner\"(\"ehr\".\"entry\".\"category\") as jsonb),'mappings') as jsonb)), 'match') \"/test\""); } @Test diff --git a/sonar-project.properties b/sonar-project.properties index e0a59da11..b7ba9e8cb 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -26,7 +26,8 @@ sonar.sources=api/src/main/java/, \ jooq-pq/src/main/java/, \ rest-ehr-scape/src/main/java/, \ rest-openehr/src/main/java/, \ - service/src/main/java/ + service/src/main/java/, \ + plugin/src/main/java sonar.java.binaries=**/target/** sonar.exclusions=**/test/** sonar.coverage.exclusions=**/test/** diff --git a/test-coverage/pom.xml b/test-coverage/pom.xml index 34d77dbd7..4532a3bd2 100644 --- a/test-coverage/pom.xml +++ b/test-coverage/pom.xml @@ -28,7 +28,7 @@ org.ehrbase.openehr server - 0.30.0 + 0.31.0 test-coverage