diff --git a/goofy-server/pom.xml b/goofy-server/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..b91dd9bea9ae8b0acef721fe7f0de085bd464395 --- /dev/null +++ b/goofy-server/pom.xml @@ -0,0 +1,251 @@ +<!-- + + Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + Ministerpräsidenten des Landes Schleswig-Holstein + Staatskanzlei + Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + + Lizenziert unter der EUPL, Version 1.2 oder - sobald + diese von der Europäischen Kommission genehmigt wurden - + Folgeversionen der EUPL ("Lizenz"); + Sie dürfen dieses Werk ausschließlich gemäß + dieser Lizenz nutzen. + Eine Kopie der Lizenz finden Sie hier: + + https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + + Sofern nicht durch anwendbare Rechtsvorschriften + gefordert oder in schriftlicher Form vereinbart, wird + die unter der Lizenz verbreitete Software "so wie sie + ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + ausdrücklich oder stillschweigend - verbreitet. + Die sprachspezifischen Genehmigungen und Beschränkungen + unter der Lizenz sind dem Lizenztext zu entnehmen. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>de.itvsh.ozg</groupId> + <artifactId>goofy</artifactId> + <version>1.1.1</version> + </parent> + + <artifactId>goofy-server</artifactId> + <name>Goofy Server</name> + <description>Projekt packaging deployment artefact</description> + <packaging>jar</packaging> + + <properties> + <maven.compiler.source>${java.version}</maven.compiler.source> + <maven.compiler.target>${java.version}</maven.compiler.target> + + <spring-boot.build-image.imageName>docker.ozg-sh.de/goofy:build-latest</spring-boot.build-image.imageName> + </properties> + + <dependencies> + <!-- Spring --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-log4j2</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-hateoas</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-validation</artifactId> + </dependency> + + <dependency> + <groupId>net.devh</groupId> + <artifactId>grpc-client-spring-boot-starter</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-spring-boot-starter</artifactId> + </dependency> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-admin-client</artifactId> + </dependency> + + <!-- jwt --> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + </dependency> + + <!-- own projects --> + <dependency> + <groupId>de.itvsh.ozg.pluto</groupId> + <artifactId>pluto-interface</artifactId> + </dependency> + <dependency> + <groupId>de.itvsh.ozg.pluto</groupId> + <artifactId>pluto-utils</artifactId> + </dependency> + <dependency> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-pdf</artifactId> + </dependency> + + <!-- tools --> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> + <dependency> + <groupId>org.jsoup</groupId> + <artifactId>jsoup</artifactId> + </dependency> + + <!-- aspectJ --> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjweaver</artifactId> + </dependency> + <dependency> + <groupId>org.aspectj</groupId> + <artifactId>aspectjrt</artifactId> + </dependency> + + <!-- Dev --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <scope>runtime</scope> + </dependency> + + <!-- commons --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <!-- Test --> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>de.itvsh.ozg.pluto</groupId> + <artifactId>pluto-utils</artifactId> + <type>test-jar</type> + <scope>test</scope> + <version>${pluto.version}</version> + </dependency> + + <dependency> + <groupId>com.thedeanda</groupId> + <artifactId>lorem</artifactId> + </dependency> + + </dependencies> + + <build> + <finalName>${project.artifactId}</finalName> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <docker> + <publishRegistry> + <username>${docker-username}</username> + <password>${docker-password}</password> + <url>${docker-url}</url> + </publishRegistry> + </docker> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>pl.project13.maven</groupId> + <artifactId>git-commit-id-plugin</artifactId> + </plugin> + + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>copy-client</id> + <phase>compile</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${project.build.directory}/classes/META-INF/resources</outputDirectory> + <resources> + <resource> + <directory>../${project.parent.artifactId}-client/dist/apps/goofy/</directory> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/goofy-server/sonar-project.properties b/goofy-server/sonar-project.properties new file mode 100644 index 0000000000000000000000000000000000000000..8660b9e44335323e02f6aaa513605e4a34635db9 --- /dev/null +++ b/goofy-server/sonar-project.properties @@ -0,0 +1,26 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +sonar.java.coveragePlugin=jacoco +sonar.jacoco.reportPath=target/jacoco.exec \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/CallBeanFactoryPostProcessor.java b/goofy-server/src/main/java/de/itvsh/goofy/CallBeanFactoryPostProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..a841a0843757820f8a61694444f52f9cbe7ee6f8 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/CallBeanFactoryPostProcessor.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class CallBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + private final CallScope callScope; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.registerScope(CallScope.SCOPE_NAME, callScope); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/CallScope.java b/goofy-server/src/main/java/de/itvsh/goofy/CallScope.java new file mode 100644 index 0000000000000000000000000000000000000000..316082488fc32a8e999d3795c966b6ca469adab5 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/CallScope.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; +import org.springframework.core.NamedInheritableThreadLocal; + +import lombok.extern.log4j.Log4j2; + +@Log4j2 +public class CallScope implements Scope { + + public static final String SCOPE_NAME = "call"; + + private static final ThreadLocal<Map<String, Object>> scopedObjectsHolder = new NamedInheritableThreadLocal<>("Call Context"); + private static final ThreadLocal<Map<String, Runnable>> destructionCallbacksHolder = new NamedInheritableThreadLocal<>( + "Call Context Desctruction Callbacks"); + + public void startScope() { + LOG.info("START Call-Scope"); + scopedObjectsHolder.set(new ConcurrentHashMap<>()); + destructionCallbacksHolder.set(new ConcurrentHashMap<>()); + } + + public void endScope() { + scopedObjectsHolder.remove(); + callAllDestructionCallbacks(); + destructionCallbacksHolder.remove(); + } + + private void callAllDestructionCallbacks() { + destructionCallbacksHolder.get().values().forEach(Runnable::run); + } + + @Override + public Object get(String name, ObjectFactory<?> objectFactory) { + if (!scopedObjectsHolder.get().containsKey(name)) { + scopedObjectsHolder.get().put(name, objectFactory.getObject()); + } + + return scopedObjectsHolder.get().get(name); + } + + @Override + public Object remove(String name) { + destructionCallbacksHolder.get().remove(name); + return scopedObjectsHolder.get().remove(name); + } + + @Override + public void registerDestructionCallback(String name, Runnable callback) { + destructionCallbacksHolder.get().put(name, callback); + + } + + @Override + public Object resolveContextualObject(String key) { + return scopedObjectsHolder.get().get(key); + } + + @Override + public String getConversationId() { + return Thread.currentThread().getName(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/EnvironmentController.java b/goofy-server/src/main/java/de/itvsh/goofy/EnvironmentController.java new file mode 100644 index 0000000000000000000000000000000000000000..16a43db83b9555b0779b225a19d15a482e5772ed --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/EnvironmentController.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/environment") +public class EnvironmentController { + + @Autowired + private KeycloakSpringBootProperties kcProperties; + + @Value("${goofy.production}") + private boolean production = true; + + @GetMapping + public FrontendEnvironment getFrontendEnvironment() { + return FrontendEnvironment.builder()// + .production(production)// + .remoteHost(apiRoot())// + .authServer(kcProperties.getAuthServerUrl())// + .clientId(kcProperties.getResource())// + .realm(kcProperties.getRealm()) + .build(); + } + + private String apiRoot() { + return linkTo(RootController.class).toUri().toString(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/FrontendEnvironment.java b/goofy-server/src/main/java/de/itvsh/goofy/FrontendEnvironment.java new file mode 100644 index 0000000000000000000000000000000000000000..2a76e62af963a538fb212c95082f0f1c6e256638 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/FrontendEnvironment.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FrontendEnvironment { + + private boolean production; + private String remoteHost; + private String authServer; + private String realm; + private String clientId; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/GoofyServerApplication.java b/goofy-server/src/main/java/de/itvsh/goofy/GoofyServerApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..5bc352f40cf7db040d951e495dcab17093f754ac --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/GoofyServerApplication.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.TimeZone; + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.security.concurrent.DelegatingSecurityContextRunnable; +import org.springframework.web.filter.ForwardedHeaderFilter; + +@SpringBootApplication +@EnableAsync +@EnableAspectJAutoProxy(proxyTargetClass = true) +@ComponentScan({ "de.itvsh.*" }) +public class GoofyServerApplication { + + public static final String GRPC_CLIENT = "pluto"; + + public static void main(String[] args) { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + SpringApplication.run(GoofyServerApplication.class, args); + } + + @Bean + public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() { + FilterRegistrationBean<ForwardedHeaderFilter> bean = new FilterRegistrationBean<>(); + bean.setFilter(new ForwardedHeaderFilter()); + return bean; + } + + @Bean + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + + executor.setThreadNamePrefix("async-"); + executor.setTaskDecorator(DelegatingSecurityContextRunnable::new); + return executor; + } + + @Bean + public CallScope callScope() { + return new CallScope(); + } + + @Bean + public BeanFactoryPostProcessor beanFactoryPostProcessor(CallScope callScope) { + return new CallBeanFactoryPostProcessor(callScope); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/JwtTokenUtil.java b/goofy-server/src/main/java/de/itvsh/goofy/JwtTokenUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..ac0bb5d7018b758d167081e698511277bd574f07 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/JwtTokenUtil.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.stereotype.Component; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; + +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.downloadtoken.DownloadTokenProperties; +import de.itvsh.goofy.common.user.UserProfile; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +@Component +public class JwtTokenUtil { + + public static final String TOKEN_TYP_KEY = "typ"; + public static final String TOKEN_TYPE = "JWT"; + public static final String TOKEN_ISSUER = "secure-api"; + public static final String TOKEN_AUDIENCE = "secure-app"; + public static final String ROLE_CLAIM = "roles"; + public static final String USERID_CLAIM = "userId"; + public static final String FIRSTNAME_CLAIM = "firstName"; + public static final String LASTNAME_CLAIM = "lastName"; + public static final String FILEID_CLAIM = "fileId"; + public static final String ORGANSIATIONSEINHEIT_IDS_CLAIM = "organisationseinheitIds"; + + @Autowired + private DownloadTokenProperties downloadTokenProperties; + + @SuppressWarnings("unchecked") + private List<Map<String, String>> getRoleClaims(String token) { + List<Map<String, String>> roleClaims = new ArrayList<>(); + + getAllClaimsFromToken(token).ifPresent(claims -> roleClaims.addAll((List<Map<String, String>>) claims.get(ROLE_CLAIM))); + + return roleClaims; + } + + public Optional<Claims> getAllClaimsFromToken(String token) { + return Optional.of(Jwts.parser().setSigningKey(downloadTokenProperties.getSecret().getBytes()).parseClaimsJws(token).getBody()); + } + + public List<SimpleGrantedAuthority> getRolesFromToken(String token) { + List<Map<String, String>> claimsList = getRoleClaims(token); + return claimsList.stream() + .flatMap(rolesMap -> rolesMap.entrySet().stream()) + .map(roleEntry -> new SimpleGrantedAuthority(roleEntry.getValue())) + .toList(); + } + + @SuppressWarnings({ "unchecked" }) + public Collection<String> getOrganisationseinheitIdsFromToken(String token) { + List<String> organisationseinheitIds = new ArrayList<>(); + getAllClaimsFromToken(token) + .ifPresent(claims -> { + if (claims.get(ORGANSIATIONSEINHEIT_IDS_CLAIM) != null) { + organisationseinheitIds.addAll((Collection<String>) claims.get(ORGANSIATIONSEINHEIT_IDS_CLAIM)); + } + }); + return organisationseinheitIds; + } + + public String generateToken(FileId fileId, UserProfile user) { + var claims = new HashMap<String, Object>(); + claims.put(USERID_CLAIM, user.getId().toString()); + claims.put(FIRSTNAME_CLAIM, user.getFirstName()); + claims.put(LASTNAME_CLAIM, user.getLastName()); + claims.put(ROLE_CLAIM, user.getAuthorities()); + claims.put(FILEID_CLAIM, fileId.toString()); + claims.put(ORGANSIATIONSEINHEIT_IDS_CLAIM, user.getOrganisationseinheitIds()); + + return doGenerateToken(claims, user.getId().toString()); + } + + private String doGenerateToken(Map<String, Object> claims, String subject) { + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setHeaderParam(TOKEN_TYP_KEY, TOKEN_TYPE) + .setIssuer(TOKEN_ISSUER).setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + downloadTokenProperties.getValidity())) + .setAudience(TOKEN_AUDIENCE) + .signWith(SignatureAlgorithm.HS512, downloadTokenProperties.getSecret().getBytes()) + .compact(); + } + + public void verifyToken(String token) throws JWTVerificationException { + var algorithm = Algorithm.HMAC512(downloadTokenProperties.getSecret()); + var verifier = JWT.require(algorithm).build(); + + verifier.verify(token); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/RequestAttributes.java b/goofy-server/src/main/java/de/itvsh/goofy/RequestAttributes.java new file mode 100644 index 0000000000000000000000000000000000000000..d7b2bf987415db144ed1a6b79c89f778399c6580 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/RequestAttributes.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.UUID; + +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Component; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Component +@Scope(scopeName = "call", proxyMode = ScopedProxyMode.TARGET_CLASS) +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor +public class RequestAttributes { + + @Builder.Default + private String requestId = UUID.randomUUID().toString(); +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/RequestIdFilter.java b/goofy-server/src/main/java/de/itvsh/goofy/RequestIdFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..430039a8c01ee0dff2d59bc013936560af2ddd88 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/RequestIdFilter.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.logging.log4j.CloseableThreadContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import lombok.extern.log4j.Log4j2; + +@Component +@Log4j2 +class RequestIdFilter extends OncePerRequestFilter { + + private static final String REQUEST_ID_KEY = "requestId"; + + @Autowired + private RequestAttributes reqAttributes; + @Autowired + private CallScope callScope; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + callScope.startScope(); + try (CloseableThreadContext.Instance ctc = CloseableThreadContext.put(REQUEST_ID_KEY, reqAttributes.getRequestId())) { + LOG.info("START of Request with ID '{}'.", reqAttributes.getRequestId()); + filterChain.doFilter(request, response); + } finally { + LOG.info("END of Request with ID '{}'", reqAttributes.getRequestId()); + callScope.endScope(); + } + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/RootController.java b/goofy-server/src/main/java/de/itvsh/goofy/RootController.java new file mode 100644 index 0000000000000000000000000000000000000000..24daf2d39e3d1586f1185d66a22124cdd3ab661e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/RootController.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.time.Instant; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.info.BuildProperties; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.downloadtoken.DownloadTokenController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.common.user.UserRemoteService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.system.SystemStatusService; +import de.itvsh.goofy.vorgang.VorgangController; + +@RestController +@RequestMapping(RootController.PATH) +public class RootController { + + static final String PATH = "/api"; // NOSONAR + + static final String REL_VORGAENGE = "vorgaenge"; + static final String REL_SEARCH = "search"; + static final String REL_SEARCH_USER = "search-user-profiles"; + static final String REL_MY_VORGAENGE = "myVorgaenge"; + static final String REL_SEARCH_MY_VORGAENGE = "searchMyVorgaenge"; + static final String REL_DOWNLOAD_TOKEN = "downloadToken"; + static final String REL_CURRENT_USER = "currentUser"; + + @Autowired(required = false) + public BuildProperties buildProperties; + @Autowired + private CurrentUserService currentUserService; + @Autowired + private SystemStatusService systemStatusService; + @Autowired + private UserRemoteService internalUserIdService; + + @Autowired + private UserManagerUrlProvider userManagerUrlProvider; + + @GetMapping + public EntityModel<RootResource> getRootResource() { + var internalUserId = internalUserIdService.getUserId(currentUserService.getUserId()); + + var modelBuilder = ModelBuilder.fromEntity(new RootResource()) + .ifMatch(this::hasRole).addLinks( + linkTo(RootController.class).withSelfRel(), + linkTo(VorgangController.class).withRel(REL_VORGAENGE), + linkTo(DownloadTokenController.class).withRel(REL_DOWNLOAD_TOKEN), + Link.of(userManagerUrlProvider.getUserProfileSearchTemplate(), REL_SEARCH_USER)) + .ifMatch(this::hasRoleAndSearchServerAvailable).addLinks( + buildVorgangListByPageLink(REL_SEARCH, Optional.empty())); + + internalUserId.ifPresent(userId -> modelBuilder + .ifMatch(this::hasVerwaltungRole).addLink( + buildVorgangListByPageLink(REL_MY_VORGAENGE, Optional.of(userId))) + .ifMatch(this::hasVerwaltungRoleAndSearchServerAvailable).addLink( + buildVorgangListByPageLink(REL_SEARCH_MY_VORGAENGE, Optional.of(userId)))); + + var model = modelBuilder.buildModel(); + + getUserProfilesUrl() + .ifPresent(urlTemplate -> model.add(Link.of(String.format(urlTemplate, currentUserService.getUserId()), REL_CURRENT_USER))); + + return model; + } + + private boolean hasRoleAndSearchServerAvailable() { + return hasRole() && systemStatusService.isSearchServerAvailable(); + } + + private boolean hasVerwaltungRoleAndSearchServerAvailable() { + return hasVerwaltungRole() && systemStatusService.isSearchServerAvailable(); + } + + boolean hasRole() { + return hasVerwaltungRole() || currentUserService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER); + } + + boolean hasVerwaltungRole() { + return currentUserService.hasRole(UserRole.VERWALTUNG_USER) + || currentUserService.hasRole(UserRole.VERWALTUNG_POSTSTELLE); + } + + private Link buildVorgangListByPageLink(String linkRel, Optional<UserId> assignedTo) { + return linkTo(methodOn(VorgangController.class).getVorgangListByPage(0, null, null, assignedTo)).withRel(linkRel); + } + + Optional<String> getUserProfilesUrl() { + if (userManagerUrlProvider.isConfiguredForUserProfile()) { + return Optional.of(userManagerUrlProvider.getUserProfileTemplate()); + } + + return Optional.empty(); + } + + class RootResource { + + public String getVersion() { + return buildProperties == null ? "--" : buildProperties.getVersion(); + } + + public Instant getBuildTime() { + return buildProperties == null ? null : buildProperties.getTime(); + } + + public String getJavaVersion() { + return System.getProperty("java.version"); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/SecurityConfiguration.java b/goofy-server/src/main/java/de/itvsh/goofy/SecurityConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..59957616d39acaa1d869fd9069c4e8005dc43b00 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/SecurityConfiguration.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import org.keycloak.adapters.springsecurity.KeycloakConfiguration; +import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; +import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; +import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode; + +import de.itvsh.goofy.common.downloadtoken.DownloadTokenAuthenticationFilter; + +@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) +@KeycloakConfiguration +public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter { + + @Autowired + private DownloadTokenAuthenticationFilter downloadTokenFilter; + + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); + + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and().authorizeRequests()// + .antMatchers(HttpMethod.GET, "/api/environment").permitAll()// + .antMatchers(HttpMethod.GET, "/assets/**").permitAll()// + .antMatchers(HttpMethod.GET, "/vorgang/**").permitAll()// + .antMatchers(HttpMethod.GET, "/search/**").permitAll()// + .antMatchers("/api").authenticated()// + .antMatchers("/api/**").authenticated()// + .antMatchers("/actuator").permitAll()// + .antMatchers("/actuator/**").permitAll()// + .antMatchers("/").permitAll()// + .antMatchers("/*").permitAll()// + .anyRequest().denyAll(); + + http.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)); + http.addFilterBefore(downloadTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) { + KeycloakAuthenticationProvider keyCloakAuthProvider = keycloakAuthenticationProvider(); + keyCloakAuthProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); + auth.authenticationProvider(keyCloakAuthProvider); + } + + @Override + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new NullAuthenticatedSessionStrategy(); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/WebConfig.java b/goofy-server/src/main/java/de/itvsh/goofy/WebConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..72090847f2c127e444ee734b5550b01301f2549f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/WebConfig.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import org.keycloak.adapters.KeycloakConfigResolver; +import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.PathResourceResolver; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private static final String RESOURCE_LOCATION = "classpath:/META-INF/resources/"; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + + registry.addResourceHandler("/*.js", "/*.css", "/*.ttf", "/*.woff", "/*.woff2", "/*.eot", + "/**/*.svg", "/*.svf", "/*.otf", "/*.ico", "/**/*.png") + .addResourceLocations(RESOURCE_LOCATION) + .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic()) + .resourceChain(true) + .addResolver(new PathResourceResolver()); + + registry.addResourceHandler("/**/*") + .addResourceLocations(RESOURCE_LOCATION) + .setCacheControl(CacheControl.noStore()) + .setUseLastModified(false) + .resourceChain(true) + .addResolver(new PathResourceResolver() { + @Override + protected Resource getResource(String resourcePath, Resource location) throws IOException { + Resource requestedResource = location.createRelative(resourcePath); + return requestedResource.exists() && requestedResource.isReadable() ? requestedResource + : super.getResource("index.html", location); + } + }); + } + + @Bean + public KeycloakConfigResolver keyCloakConfigResolver() { + return new KeycloakSpringBootConfigResolver(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/attachment/AttachmentController.java b/goofy-server/src/main/java/de/itvsh/goofy/attachment/AttachmentController.java new file mode 100644 index 0000000000000000000000000000000000000000..719334be8b9930e45df8ddfc2972f576c5ea0351 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/attachment/AttachmentController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.attachment; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.binaryfile.BinaryFileModelAssembler; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileService; + +@RestController +@RequestMapping(AttachmentController.ATTACHMENT_PATH) +public class AttachmentController { + + static final String ATTACHMENT_PATH = "/api/attachments"; // NOSONAR + + static final String PARAM_EINGANG_ID = "eingangId"; + + @Autowired + private OzgFileService fileService; + @Autowired + private BinaryFileModelAssembler modelAssembler; + + @GetMapping(params = PARAM_EINGANG_ID) + public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { + return modelAssembler.toCollectionModel(fileService.getAttachmentsByEingang(eingangId)); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceDeserializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..3100089a888c3237b5ef6869c8d502101e5d9af1 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceDeserializer.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import de.itvsh.kop.common.datatype.StringBasedValue; +import lombok.Getter; + +abstract class AbstractLinkedResourceDeserializer extends StdDeserializer<Object> implements ContextualDeserializer { + + private static final long serialVersionUID = 1L; + + @Getter + private BeanProperty beanProperty; + + @Getter + private final JavaType targetType; + + protected AbstractLinkedResourceDeserializer() { + super(Object.class); + targetType = null; + } + + protected AbstractLinkedResourceDeserializer(BeanProperty beanProperty) { + super(Object.class); + this.beanProperty = beanProperty; + this.targetType = beanProperty.getType(); + } + + @Override + public Object deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + if (jsonParser.isExpectedStartArrayToken()) { + Collection<Object> idList = targetType.getRawClass().isAssignableFrom(Set.class) ? new HashSet<>() : new ArrayList<>(); + + while (!jsonParser.nextToken().isStructEnd()) { + idList.add(extractId(jsonParser.getText())); + } + return idList; + } else { + return extractId(jsonParser.getText()); + } + } + + Object extractId(String url) { + Class<?> type; + if (targetType.isCollectionLikeType()) { + type = targetType.getContentType().getRawClass(); + } else { + type = targetType.getRawClass(); + } + + if (String.class.isAssignableFrom(type)) { + return extractStringId(url); + } + if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { + return extractLongId(url); + } + if (StringBasedValue.class.isAssignableFrom(type)) { + return extractStringBasedValue(type, url); + } + return buildByBuilder(url); + } + + abstract Object buildByBuilder(String url); + + public static Long extractLongId(String uri) { + var trimedUri = StringUtils.trimToNull(uri); + if (Objects.isNull(trimedUri)) { + return null; + } + return Long.parseLong(URLDecoder.decode(trimedUri.substring(trimedUri.lastIndexOf('/') + 1), StandardCharsets.UTF_8)); + } + + private StringBasedValue extractStringBasedValue(Class<?> type, String url) { + String value = extractStringId(url); + Method fromMethod; + try { + fromMethod = type.getMethod("from", String.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException( + String.format("Cannot generate Id from type '%s'. Missing 'from' Method.", targetType.getRawClass().getSimpleName())); + } + try { + return (StringBasedValue) fromMethod.invoke(null, value); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException( + String.format("Cannot generate Id from type '%s'. Error calling 'from' Method.", targetType.getRawClass().getSimpleName()), + e); + } + } + + public static String extractStringId(String url) { + return URLDecoder.decode(url.substring(url.lastIndexOf('/') + 1), StandardCharsets.UTF_8); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceSerializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..29c8fd6b30413864b65bc6964efcfc30e3a6f2e0 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/AbstractLinkedResourceSerializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +abstract class AbstractLinkedResourceSerializer extends JsonSerializer<Object> implements ContextualSerializer { + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + + if (value instanceof Collection) { + gen.writeStartArray(); + ((Collection<?>) value).forEach(val -> writeObject(gen, buildLink(val))); + gen.writeEndArray(); + } else { + writeObject(gen, buildLink(value)); + } + } + + abstract String buildLink(Object id); + + abstract IdExtractor<Object> getExtractor(); + + void writeObject(JsonGenerator gen, Object value) { + try { + gen.writeObject(value); + } catch (IOException e) { + throw new TechnicalException("Error writing String to json", e); + } + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/BaseTypesMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/BaseTypesMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6e103714dab75d63ca95542a2fe045652aefde59 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/BaseTypesMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.time.ZonedDateTime; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; + +@Mapper +public interface BaseTypesMapper { + + default String fromString(String in) { + return StringUtils.trimToNull(in); + } + + default ZonedDateTime mapDateTime(String inStr) { + return Optional.ofNullable(fromString(inStr)).map(ZonedDateTime::parse).orElse(null); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/GrpcUtil.java b/goofy-server/src/main/java/de/itvsh/goofy/common/GrpcUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..640d522dc9c046801df440cc0d89eddd25b2c8d2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/GrpcUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Optional; +import java.util.stream.StreamSupport; + +import io.grpc.Metadata; +import io.grpc.Metadata.Key; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GrpcUtil { + + public static Key<byte[]> createKeyOf(String key) { + return Key.of(key, Metadata.BINARY_BYTE_MARSHALLER); + } + + public static String getFromHeaders(String key, Metadata headers) { + return Optional.ofNullable(headers.get(createKeyOf(key))) + .map(val -> new String(val, StandardCharsets.UTF_8)) + .orElse(null); + } + + public static Collection<String> getCollection(String key, Metadata headers) { + return StreamSupport.stream(headers.getAll(createKeyOf(key)).spliterator(), false) + .map(bytes -> new String(bytes, StandardCharsets.UTF_8)) + .toList(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/IdBuilder.java b/goofy-server/src/main/java/de/itvsh/goofy/common/IdBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..545739c2c2e4b279b631268a607a4b4d9a206c8e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/IdBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import com.fasterxml.jackson.databind.BeanProperty; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +class IdBuilder implements ObjectBuilder<Object> { + + @Override + public Object build(Object id) { + return id; + } + + @Override + public ObjectBuilder<Object> constructContextAware(BeanProperty property) { + return new IdBuilder(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/IdExtractor.java b/goofy-server/src/main/java/de/itvsh/goofy/common/IdExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..6fc433669c084148897e8dbdaa83d4c3ae9fd6d1 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/IdExtractor.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +@FunctionalInterface +public interface IdExtractor<T> { + String extractId(T object); +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/IgnoreGrpcFields.java b/goofy-server/src/main/java/de/itvsh/goofy/common/IgnoreGrpcFields.java new file mode 100644 index 0000000000000000000000000000000000000000..6f15affe1f440110f3c135dff984d78cb7acd1c2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/IgnoreGrpcFields.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import org.mapstruct.Mapping; + +@Mapping(target = "mergeFrom", ignore = true) +@Mapping(target = "clearField", ignore = true) +@Mapping(target = "clearOneof", ignore = true) +@Mapping(target = "mergeUnknownFields", ignore = true) +@Mapping(target = "unknownFields", ignore = true) +@Mapping(target = "allFields", ignore = true) +@Mapping(target = "createdAtBytes", ignore = true) +@Mapping(target = "createdByBytes", ignore = true) +@Mapping(target = "createdByNameBytes", ignore = true) +@Mapping(target = "idBytes", ignore = true) +public @interface IgnoreGrpcFields { +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResource.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResource.java new file mode 100644 index 0000000000000000000000000000000000000000..668e4eae7e000fedf87dbae222204277cd366bef --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResource.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Inherited + +@JacksonAnnotationsInside +@JsonSerialize(using = LinkedResourceSerializer.class) +@JsonDeserialize(using = LinkedResourceDeserializer.class) +public @interface LinkedResource { + + Class<?> controllerClass(); + + Class<? extends IdExtractor<Object>> extractor() default ToStringExtractor.class; + + Class<? extends ObjectBuilder<Object>> builder() default IdBuilder.class; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceDeserializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..9fcf9f0fe1c95f4f39e7557824ff487e8896a5c9 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceDeserializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +public class LinkedResourceDeserializer extends AbstractLinkedResourceDeserializer { + + private static final long serialVersionUID = 1L; + + private LinkedResource annotation; + + protected LinkedResourceDeserializer() { + super(); + } + + protected LinkedResourceDeserializer(BeanProperty beanProperty) { + super(beanProperty); + this.annotation = beanProperty.getAnnotation(LinkedResource.class); + } + + @Override + Object buildByBuilder(String url) { + ObjectBuilder<?> builder; + try { + builder = ConstructorUtils.invokeConstructor(annotation.builder()).constructContextAware(getBeanProperty()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating Builder.", e); + } + return builder.build(extractStringId(url)); + } + + @Override + public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) { + return new LinkedResourceDeserializer(property); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceSerializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..0bc2e4c58dc4f6c643ea02de0582b21a6a3995b5 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedResourceSerializer.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class LinkedResourceSerializer extends AbstractLinkedResourceSerializer { + + private LinkedResource annotation; + + private LinkedResourceSerializer(LinkedResource annotation) { + this.annotation = annotation; + } + + @Override + public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) { + return new LinkedResourceSerializer(property.getAnnotation(LinkedResource.class)); + } + + @Override + String buildLink(Object id) { + return linkTo(annotation.controllerClass()).slash(getExtractor().extractId(id)).toString(); + } + + @Override + IdExtractor<Object> getExtractor() { + try { + return ConstructorUtils.invokeConstructor(annotation.extractor()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating IdExtractor", e); + } + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResource.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResource.java new file mode 100644 index 0000000000000000000000000000000000000000..8a08a1898bf2a1f3dfc5b392cfab02ead1dcba2a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResource.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@Inherited + +@JacksonAnnotationsInside +@JsonSerialize(using = LinkedUserProfileResourceSerializer.class) +@JsonDeserialize(using = LinkedUserProfileResourceDeserializer.class) +public @interface LinkedUserProfileResource { + + Class<? extends IdExtractor<Object>> extractor() default ToStringExtractor.class; + + Class<? extends ObjectBuilder<Object>> builder() default IdBuilder.class; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..a8ed967a5bd64ddb3f859ea4310a20fac949dab6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializer.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.lang3.reflect.ConstructorUtils; + +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +public class LinkedUserProfileResourceDeserializer extends AbstractLinkedResourceDeserializer { + + private static final long serialVersionUID = 1L; + + private LinkedUserProfileResource annotation; + + protected LinkedUserProfileResourceDeserializer() { + super(); + } + + protected LinkedUserProfileResourceDeserializer(BeanProperty beanProperty) { + super(beanProperty); + this.annotation = beanProperty.getAnnotation(LinkedUserProfileResource.class); + } + + @Override + Object buildByBuilder(String url) { + ObjectBuilder<?> builder; + try { + builder = ConstructorUtils.invokeConstructor(annotation.builder()).constructContextAware(getBeanProperty()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating Builder.", e); + } + return builder.build(extractStringId(url)); + } + + @Override + public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) { + return new LinkedUserProfileResourceDeserializer(property); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializer.java b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..df1eea531784bf4be748500077486b6de0984d56 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializer.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +import org.apache.commons.lang3.reflect.ConstructorUtils; +import org.springframework.hateoas.Link; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public class LinkedUserProfileResourceSerializer extends JsonSerializer<Object> implements ContextualSerializer { + + private LinkedUserProfileResource annotation; + + private LinkedUserProfileResourceSerializer(LinkedUserProfileResource annotation) { + this.annotation = annotation; + } + + @Override + public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) { + return new LinkedUserProfileResourceSerializer(property.getAnnotation(LinkedUserProfileResource.class)); + } + + String buildLink(Object id) { + if (UserProfileUrlProvider.isConfigured()) { + return Link.of(UserProfileUrlProvider.getUrl(getExtractor().extractId(id))).getHref(); + } else { + return id.toString(); + } + } + + IdExtractor<Object> getExtractor() { + try { + return ConstructorUtils.invokeConstructor(annotation.extractor()); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new TechnicalException("Error instanciating IdExtractor", e); + } + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (value instanceof Collection) { + gen.writeStartArray(); + ((Collection<?>) value).forEach(val -> writeObject(gen, buildLink(val))); + gen.writeEndArray(); + } else { + writeObject(gen, buildLink(value)); + } + } + + void writeObject(JsonGenerator gen, Object value) { + try { + gen.writeObject(value); + } catch (IOException e) { + throw new TechnicalException("Error writing String to json", e); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ModelBuilder.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ModelBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..b484c19bdbdda160d7e2ab085678b792580688bd --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ModelBuilder.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; + +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +public class ModelBuilder<T> { + + private static final Map<Class<?>, Map<Object, List<Field>>> ANNOTATED_FIELDS_BY_ANNOTATION = new ConcurrentHashMap<>(); + + private final T entity; + private final EntityModel<T> model; + + private final List<Link> links = new LinkedList<>(); + private final List<Function<EntityModel<T>, EntityModel<T>>> mapper = new LinkedList<>(); + + private ModelBuilder(T entity) { + this.entity = entity; + this.model = null; + } + + private ModelBuilder(EntityModel<T> model) { + this.entity = null; + this.model = model; + } + + public static <T> ModelBuilder<T> fromEntity(T entity) { + return new ModelBuilder<>(entity); + } + + public static <T> ModelBuilder<T> fromModel(EntityModel<T> model) { + return new ModelBuilder<>(model); + } + + public ModelBuilder<T> addLink(Link link) { + links.add(link); + return this; + } + + public ModelBuilder<T> addLinks(Link... links) { + this.links.addAll(Arrays.asList(links)); + return this; + } + + public ModelBuilder<T> addLinks(Collection<Link> links) { + this.links.addAll(links); + return this; + } + + public ConditionalLinkAdder ifMatch(Predicate<T> predicate) { + return new ConditionalLinkAdder(predicate.test(Objects.isNull(entity) ? model.getContent() : entity)); + } + + public ConditionalLinkAdder ifMatch(BooleanSupplier guard) { + return new ConditionalLinkAdder(guard.getAsBoolean()); + } + + public ModelBuilder<T> map(UnaryOperator<EntityModel<T>> mapper) { + this.mapper.add(mapper); + return this; + } + + public EntityModel<T> buildModel() { + var filteredLinks = links.stream().filter(Objects::nonNull).collect(Collectors.toSet()); + + EntityModel<T> buildedModel = Objects.isNull(model) ? EntityModel.of(entity) : model; + buildedModel = buildedModel.add(filteredLinks); + + addLinkByLinkedResourceAnnotationIfMissing(buildedModel); + addLinkByLinkedUserProfileResourceAnnotationIfMissing(buildedModel); + + return applyMapper(buildedModel); + } + + private EntityModel<T> applyMapper(EntityModel<T> resource) { + Iterator<Function<EntityModel<T>, EntityModel<T>>> i = mapper.iterator(); + EntityModel<T> result = resource; + while (i.hasNext()) { + result = i.next().apply(result); + } + return result; + } + + private void addLinkByLinkedResourceAnnotationIfMissing(EntityModel<T> resource) { + getFields(LinkedResource.class).stream() + .filter(field -> shouldAddLink(resource, field)) + .forEach(field -> handleLinkedResourceField(resource, field)); + } + + private void handleLinkedResourceField(EntityModel<T> resource, Field field) { + getEntityFieldValue(field).ifPresent(val -> resource + .add(WebMvcLinkBuilder.linkTo(field.getAnnotation(LinkedResource.class).controllerClass()).slash(val) + .withRel(sanitizeName(field.getName())))); + } + + private void addLinkByLinkedUserProfileResourceAnnotationIfMissing(EntityModel<T> resource) { + getFields(LinkedUserProfileResource.class).stream() + .filter(field -> shouldAddLink(resource, field)) + .forEach(field -> handleLinkedUserProfileResourceField(resource, field)); + } + + private void handleLinkedUserProfileResourceField(EntityModel<T> resource, Field field) { + getEntityFieldValue(field).ifPresent(val -> { + if (UserProfileUrlProvider.isConfigured()) { + resource.add(Link.of(UserProfileUrlProvider.getUrl(val)).withRel(sanitizeName(field.getName()))); + } + }); + } + + private boolean shouldAddLink(EntityModel<T> resource, Field field) { + return !(field.getType().isArray() || Collection.class.isAssignableFrom(field.getType()) || resource.hasLink(sanitizeName(field.getName()))); + } + + private List<Field> getFields(Class<? extends Annotation> annotationClass) { + var fields = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass())) + .map(fieldsByAnnotation -> fieldsByAnnotation.get(annotationClass)) + .orElseGet(Collections::emptyList); + + if (CollectionUtils.isEmpty(fields)) { + fields = FieldUtils.getFieldsListWithAnnotation(getEntity().getClass(), annotationClass); + + updateFields(annotationClass, fields); + } + return fields; + } + + private void updateFields(Class<? extends Annotation> annotationClass, List<Field> fields) { + var annotationMap = Optional.ofNullable(ANNOTATED_FIELDS_BY_ANNOTATION.get(getEntity().getClass())).orElseGet(HashMap::new); + annotationMap.put(annotationClass, fields); + + ANNOTATED_FIELDS_BY_ANNOTATION.put(annotationClass, annotationMap); + } + + private String sanitizeName(String fieldName) { + if (fieldName.endsWith("Id")) { + return fieldName.substring(0, fieldName.indexOf("Id")); + } + return fieldName; + } + + private Optional<Object> getEntityFieldValue(Field field) { + try { + field.setAccessible(true); // NOSONAR + Optional<Object> value = Optional.ofNullable(field.get(getEntity())); + field.setAccessible(false); + return value; + } catch (IllegalArgumentException | IllegalAccessException e) { + LOG.warn("Cannot access field value of LinkedResource field.", e); + } + return Optional.empty(); + } + + private T getEntity() { + return Optional.ofNullable(entity == null ? model.getContent() : entity) + .orElseThrow(() -> new IllegalStateException("Entity must not null for ModelBuilding")); + } + + @RequiredArgsConstructor + public class ConditionalLinkAdder { + + public final boolean conditionFulfilled; + + public ModelBuilder<T> addLink(Supplier<Link> linkSupplier) { + if (conditionFulfilled) { + addLink(linkSupplier.get()); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLink(Function<T, Link> linkBuilder) { + if (conditionFulfilled) { + addLink(linkBuilder.apply(getEntity())); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLink(Link link) { + if (conditionFulfilled) { + links.add(link); + } + return ModelBuilder.this; + } + + public ModelBuilder<T> addLinks(Link... links) { + if (conditionFulfilled) { + ModelBuilder.this.links.addAll(Arrays.asList(links)); + } + return ModelBuilder.this; + } + + @SafeVarargs + public final ModelBuilder<T> addLinks(Supplier<Link>... linkSuppliers) { + if (conditionFulfilled) { + for (int i = 0; i < linkSuppliers.length; i++) { + ModelBuilder.this.links.add(linkSuppliers[i].get()); + } + } + return ModelBuilder.this; + } + + public final ModelBuilder<T> addLinks(Supplier<Collection<Link>> linksSupplier) { + if (conditionFulfilled) { + linksSupplier.get().forEach(ModelBuilder.this.links::add); + } + + return ModelBuilder.this; + } + + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ObjectBuilder.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ObjectBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..b5448bbb245decd6d7c11d9f3eb847bf01f1acb5 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ObjectBuilder.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import com.fasterxml.jackson.databind.BeanProperty; + +public interface ObjectBuilder<T> { + + T build(Object id); + + ObjectBuilder<T> constructContextAware(BeanProperty property); +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/RegexUtil.java b/goofy-server/src/main/java/de/itvsh/goofy/common/RegexUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..a15c248f7bb4e56d1ef948e54b3bcf6a74bfa597 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/RegexUtil.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RegexUtil { + + public static final String VALIDATION_EMAIL = "[a-zA-Z0-9!#$%&'*+\\/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?"; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/TimeMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/TimeMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..f2e2d8779573a36b08e00474244ab86b19ec650e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/TimeMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.time.ZonedDateTime; + +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; + +@Mapper +public interface TimeMapper { + + default ZonedDateTime parseString(String dateTimeStr) { + return StringUtils.isBlank(dateTimeStr) ? null : ZonedDateTime.parse(dateTimeStr); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ToStringExtractor.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ToStringExtractor.java new file mode 100644 index 0000000000000000000000000000000000000000..bc2b697ccacffc9e1b0f055a317fc1746057b45e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ToStringExtractor.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import lombok.NoArgsConstructor; + +@NoArgsConstructor +class ToStringExtractor implements IdExtractor<Object> { + + @Override + public String extractId(Object object) { + return object.toString(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/UserProfileUrlProvider.java b/goofy-server/src/main/java/de/itvsh/goofy/common/UserProfileUrlProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..97aacab5e2086ac32bb9de4c491fb105acedd1fb --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/UserProfileUrlProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import java.util.Objects; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import com.google.common.base.Preconditions; + +@Component +public class UserProfileUrlProvider implements ApplicationContextAware { + // TODO auf javax.validation umstellen + private static final String ERROR_MESSAGE = "Key %s missing, please add it to the application.yml"; + // TODO applicationContext darf nicht static abgelegt werden - es sollte möglich + // sein, dass eine andere Konfiguration in der jvm läuft. + private static ApplicationContext applicationContext; + static final String URL_ROOT_KEY = "kop.user-manager.url"; + static final String USER_PROFILES_TEMPLATE_KEY = "kop.user-manager.profile-template"; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + UserProfileUrlProvider.setSpringContext(applicationContext); + } + + private static void setSpringContext(ApplicationContext context) { + applicationContext = context; + } + + public static boolean isConfigured() { + return Objects.nonNull(applicationContext.getEnvironment().getProperty(URL_ROOT_KEY)) + && Objects.nonNull(applicationContext.getEnvironment().getProperty(USER_PROFILES_TEMPLATE_KEY)); + } + + public static String getUrl(Object val) { + // TODO Abhängingkeit zu com.google.common ausbauen + Preconditions.checkNotNull(applicationContext, "ApplicationContext not initialized"); + + // TODO parameter lieber injezieren und validation verwenden + var rootUrl = applicationContext.getEnvironment().getProperty(URL_ROOT_KEY); + Preconditions.checkNotNull(rootUrl, ERROR_MESSAGE, URL_ROOT_KEY); + + var template = applicationContext.getEnvironment().getProperty(USER_PROFILES_TEMPLATE_KEY); + Preconditions.checkNotNull(template, ERROR_MESSAGE, USER_PROFILES_TEMPLATE_KEY); + + // TODO UriComponent Builder verwenden + var profilesUrl = rootUrl + template; + return String.format(profilesUrl, val); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java new file mode 100644 index 0000000000000000000000000000000000000000..15e2dd27289f97ba18d823fc7ed03e0d93d38878 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/ValidationMessageCodes.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ValidationMessageCodes { + + private static final String FIELD_PREFIX = "validation_field_"; + public static final String FIELD_IS_EMPTY = FIELD_PREFIX + "empty"; + public static final String FIELD_MIN_SIZE = FIELD_PREFIX + "min_size"; + public static final String FIELD_MAX_SIZE = FIELD_PREFIX + "max_size"; + public static final String FIELD_SIZE = FIELD_PREFIX + "size"; + public static final String FIELD_DATE_PAST = FIELD_PREFIX + "date_past"; + public static final String FIELD_INVALID = FIELD_PREFIX + "invalid"; + public static final String FIELD_FILE_SIZE_EXCEEDED = FIELD_PREFIX + "file_size_exceeded"; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItem.java b/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItem.java new file mode 100644 index 0000000000000000000000000000000000000000..8354a1dfb90f7527c7b8d0274513fc760d631099 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItem.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.attacheditem; + +import java.util.Map; + +import de.itvsh.goofy.common.command.CommandBody; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class VorgangAttachedItem implements CommandBody { + + private String id; + private long version; + + private String client; + private String vorgangId; + private String itemName; + + private Map<String, Object> item; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemService.java new file mode 100644 index 0000000000000000000000000000000000000000..2fa150af4b662917ac84ea2fa9bab5777bee7900 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemService.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.attacheditem; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandBody; +import de.itvsh.goofy.common.command.CommandBodyMapper; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.kommentar.Kommentar; +import de.itvsh.goofy.wiedervorlage.Wiedervorlage; + +@Service +public class VorgangAttachedItemService { + + private static final String DONE_FIELD_NAME = "done"; + @Autowired + private CommandBodyMapper commandBodyMapper; + @Autowired + private CommandService commandService; + + static final String WIEDERVORLAGE_ITEM_NAME = "Wiedervorlage"; + static final String KOMMENTAR_ITEM_NAME = "Kommentar"; + + public Command createNewWiedervorlage(Wiedervorlage wiedervorlage, String vorgangId) { + var vorgangAttachedItem = buildVorgangAttachedItem(wiedervorlage, vorgangId, WIEDERVORLAGE_ITEM_NAME); + + return commandService.createCommand(buildCreateCommand(vorgangId, vorgangAttachedItem)); + } + + public Command createNewKommentar(Kommentar kommentar, String vorgangId) { + var vorgangAttachedItem = buildVorgangAttachedItem(kommentar, vorgangId, KOMMENTAR_ITEM_NAME); + + return commandService.createCommand(buildCreateCommand(vorgangId, vorgangAttachedItem)); + } + + CreateCommand buildCreateCommand(String vorgangId, CommandBody body) { + return CreateCommand.builder() + .vorgangId(vorgangId) + .relationId(vorgangId) + .order(CommandOrder.CREATE_ATTACHED_ITEM) + .body(body) + .build(); + } + + public Command editKommentar(Kommentar kommentar, String kommentarId, long kommentarVersion) { + var vorgangAttachedItem = buildVorgangAttachedItem(kommentar, kommentar.getVorgangId(), KOMMENTAR_ITEM_NAME); + + return commandService.createCommand( + buildUpdateCommand(kommentar.getVorgangId(), kommentarId, vorgangAttachedItem), kommentarVersion); + } + + public Command editWiedervorlage(Wiedervorlage wiedervorlage, String wiedervorlageId, long version) { + var vorgangAttachedItem = buildVorgangAttachedItem(wiedervorlage, wiedervorlage.getVorgangId(), WIEDERVORLAGE_ITEM_NAME); + + return commandService.createCommand( + buildUpdateCommand(wiedervorlage.getVorgangId(), wiedervorlageId, vorgangAttachedItem), version); + } + + VorgangAttachedItem buildVorgangAttachedItem(CommandBody body, String vorgangId, String itemName) { + return VorgangAttachedItem.builder() + .vorgangId(vorgangId) + .itemName(itemName) + .item(commandBodyMapper.fromObjectToMap(body)) + .build(); + } + + CreateCommand buildUpdateCommand(String vorgangId, String relationId, CommandBody body) { + return CreateCommand.builder() + .vorgangId(vorgangId) + .relationId(relationId) + .order(CommandOrder.UPDATE_ATTACHED_ITEM) + .body(body) + .build(); + } + + public Command setWiedervorlageDone(Wiedervorlage wiedervorlage, boolean done) { + return commandService.createCommand(createPatchCommand(wiedervorlage, done), wiedervorlage.getVersion()); + } + + CreateCommand createPatchCommand(Wiedervorlage wiedervorlage, boolean done) { + return getPatchCommandBuilder(wiedervorlage).body(createSetDoneBody(done)).relationVersion(wiedervorlage.getVersion()).build(); + } + + CreateCommand.CreateCommandBuilder getPatchCommandBuilder(Wiedervorlage wiedervorlage) { + return CreateCommand.builder() + .relationId(wiedervorlage.getId()) + .vorgangId(wiedervorlage.getVorgangId()) + .order(CommandOrder.PATCH_ATTACHED_ITEM); + } + + private VorgangAttachedItem createSetDoneBody(boolean value) { + return VorgangAttachedItem.builder().itemName(Wiedervorlage.class.getSimpleName()).item(Map.of(DONE_FIELD_NAME, value)).build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java new file mode 100644 index 0000000000000000000000000000000000000000..622ff627e59da32a86efa4cc548d4f90a78780c3 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileController.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.Future; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.kop.common.errorhandling.TechnicalException; + +@RestController +@RequestMapping(BinaryFileController.PATH) +public class BinaryFileController { + + static final String PATH = "/api/binaryFiles"; // NOSONAR + + @Autowired + private BinaryFileService service; + @Autowired + private BinaryFileModelAssembler modelAssembler; + @Autowired + private FileIdMapper fileIdMapper; + + @GetMapping("/{fileId}") + public EntityModel<OzgFile> getFile(@PathVariable FileId fileId) { + return modelAssembler.toModel(service.getFile(fileId)); + } + + @PostMapping("/{vorgangId}/{field}/file") + public Future<ResponseEntity<Void>> uploadFile(@PathVariable String vorgangId, @PathVariable String field, @RequestPart MultipartFile file) { + var fileIdFuture = service.uploadFile(buildBinaryFileUploadRequest(vorgangId, field, file)); + + return fileIdFuture.thenApply(fileId -> ResponseEntity.created(linkTo(BinaryFileController.class).slash(fileId).toUri()).build()); + } + + UploadBinaryFileRequest buildBinaryFileUploadRequest(String vorgangId, String field, MultipartFile file) { + return UploadBinaryFileRequest.builder() + .vorgangId(vorgangId) + .field(field) + .uploadStream(getInputStream(file)) + .name(file.getOriginalFilename()) + .contentType(file.getContentType()) + .size(file.getSize()) + .build(); + } + + InputStream getInputStream(MultipartFile file) { + try { + return file.getInputStream(); + } catch (IOException e) { + throw new TechnicalException("Error reading File data as stream.", e); + } + } + + public CollectionModel<EntityModel<OzgFile>> getFiles(List<FileId> fileIds) { + var files = service.getFiles(mapToFileId(fileIds)).toList(); + + return modelAssembler.toCollectionModel(files); + } + + private List<FileId> mapToFileId(List<FileId> fileIds) { + return fileIds.stream() + .map(fileIdMapper::toString) + .map(FileId::from) + .toList(); + } + + @PreAuthorize("@downloadAuthenticationHandler.canDownloadFile(#fileId)") + @GetMapping(value = "/{fileId}", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE, + "images/*" }, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public ResponseEntity<StreamingResponseBody> getFileData(@PathVariable FileId fileId) { + var ozgFile = service.getFile(fileId); + + return buildResponseEntity(ozgFile, createDownloadStreamingBody(fileId)); + } + + StreamingResponseBody createDownloadStreamingBody(FileId fileId) { + return out -> service.writeFileContent(fileId, out); + } + + ResponseEntity<StreamingResponseBody> buildResponseEntity(OzgFile ozgFile, StreamingResponseBody responseBody) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", ozgFile.getName())) + .contentLength(ozgFile.getSize()) + .contentType(MediaType.valueOf(ozgFile.getContentType())) + .body(responseBody); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserver.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..853dac311eaeb1c9dff2e5c7c15e1697444133aa --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserver.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.CompletableFuture; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataResponse; +import io.grpc.stub.StreamObserver; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +class BinaryFileDownloadStreamObserver implements StreamObserver<GrpcGetBinaryFileDataResponse> { + + private final CompletableFuture<Boolean> streamFuture; + private final OutputStream out; + + @Override + public void onNext(GrpcGetBinaryFileDataResponse response) { + writeToStream(response.getFileContent().toByteArray()); + } + + void writeToStream(byte[] contentPart) { + try { + out.write(contentPart); + } catch (IOException e) { + throw new TechnicalException("Download file error writing on outputstream", e); + } + } + + @Override + public void onError(Throwable t) { + streamFuture.completeExceptionally(t); + } + + @Override + public void onCompleted() { + streamFuture.complete(true); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..1634bda97152baa66dbe4050471dacbd8498f1ff --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileMaxSizeConstraint.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import de.itvsh.goofy.common.ValidationMessageCodes; + +@Constraint(validatedBy = { UploadBinaryFileSizeValidator.class }) +@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE }) +@Retention(RUNTIME) +@Documented +public @interface BinaryFileMaxSizeConstraint { + String message() default ValidationMessageCodes.FIELD_FILE_SIZE_EXCEEDED; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; + + int max() default UploadBinaryFileSizeValidator.DEFAULT_MAX_SIZE; + + String unit() default UploadBinaryFileSizeValidator.DEFAULT_UNIT_STRING; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..db7613cc2b040b135046914a58aca170cbd6d84c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssembler.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.file.OzgFile; + +@Component +public class BinaryFileModelAssembler implements RepresentationModelAssembler<OzgFile, EntityModel<OzgFile>> { + + static final String REL_DOWNLOAD = "download"; + + @Override + public EntityModel<OzgFile> toModel(OzgFile file) { + var selfLink = linkTo(BinaryFileController.class).slash(file.getId()); + + return ModelBuilder.fromEntity(file).addLink(selfLink.withSelfRel()) + .addLink(linkTo(BinaryFileController.class).slash(file.getId()).withRel(REL_DOWNLOAD)) + .buildModel(); + } + + public CollectionModel<EntityModel<OzgFile>> toCollectionModel(Stream<OzgFile> entities) { + return CollectionModel.of(entities.map(this::toModel).collect(Collectors.toList()), + linkTo(BinaryFileController.class).withSelfRel()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..2f309fa3d5387c3a19a206beab9b6b0a118183d8 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileProperties.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.Collections; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.unit.DataSize; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(BinaryFileProperties.PREFIX) +class BinaryFileProperties { + + static final String PREFIX = "kop.upload"; + + private Map<String, DataSize> maxFileSize = Collections.emptyMap(); + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..9267bb00fc4fac00d2b71a73b17f546f3eef3da6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteService.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.io.OutputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.google.protobuf.ByteString; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileMapper; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.ozg.pluto.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcBinaryFilesRequest; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcFindFilesResponse; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataRequest; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest; +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class BinaryFileRemoteService { + + @GrpcClient("pluto") + private BinaryFileServiceBlockingStub serviceStub; + @GrpcClient("pluto") + private BinaryFileServiceStub asyncServiceStub; + @Autowired + private OzgFileMapper mapper; + @Autowired + private FileIdMapper fileIdMapper; + + @Autowired + private ContextService contextService; + + static final int CHUNK_SIZE = 255 * 1024; + + public OzgFile getFile(FileId fileId) { + var response = serviceStub.findBinaryFilesMetaData(buildGrpcBinaryFileRequest(fileId)); + + return mapOzgFile(response.getFile(0)); + } + + private OzgFile mapOzgFile(GrpcOzgFile file) { + return mapper.toFile(file); + } + + GrpcBinaryFilesRequest buildGrpcBinaryFileRequest(FileId fileId) { + return GrpcBinaryFilesRequest.newBuilder() + .addFileId(fileId.toString()) + .setContext(contextService.createCallContext()) + .build(); + } + + public CompletableFuture<FileId> uploadFile(UploadBinaryFileRequest uploadBinaryFileRequest) { + var fileIdFuture = new CompletableFuture<FileId>(); + var responseObserver = createUploadBinaryFileObserver(fileIdFuture, uploadBinaryFileRequest); + + asyncServiceStub.uploadBinaryFileAsStream(responseObserver); + + return fileIdFuture; + } + + BinaryFileUploadStreamObserver createUploadBinaryFileObserver(CompletableFuture<FileId> fileIdFuture, UploadBinaryFileRequest uploadBinaryFileRequest) { + var metadataRequest = buildMetaDataRequest(uploadBinaryFileRequest); + var streamer = new ChunkedFileSender<>(uploadBinaryFileRequest.getUploadStream(), CHUNK_SIZE, this::buildChunkRequest, metadataRequest); + return new BinaryFileUploadStreamObserver(fileIdFuture, streamer); + } + + GrpcUploadBinaryFileRequest buildMetaDataRequest(UploadBinaryFileRequest uploadBinaryFileRequest) { + return GrpcUploadBinaryFileRequest.newBuilder() + .setMetadata(GrpcUploadBinaryFileMetaData.newBuilder() + .setContext(contextService.createCallContext()) + .setVorgangId(uploadBinaryFileRequest.getVorgangId()) + .setField(uploadBinaryFileRequest.getField()) + .setContentType(uploadBinaryFileRequest.getContentType()) + .setSize(uploadBinaryFileRequest.getSize()) + .setFileName(uploadBinaryFileRequest.getName())) + .build(); + } + + GrpcUploadBinaryFileRequest buildChunkRequest(byte[] bytes) { + return GrpcUploadBinaryFileRequest.newBuilder().setFileContent((ByteString.copyFrom(bytes))).build(); + } + + public Stream<OzgFile> getFiles(List<FileId> fileIds) { + var response = serviceStub.findBinaryFilesMetaData(buildBinaryFilesRequest(fileIds)); + + return mapGrpcFindFilesRespones(response); + } + + private Stream<OzgFile> mapGrpcFindFilesRespones(GrpcFindFilesResponse response) { + return response.getFileList().stream().map(mapper::toFile); + } + + GrpcBinaryFilesRequest buildBinaryFilesRequest(List<FileId> fileIds) { + return GrpcBinaryFilesRequest.newBuilder() + .addAllFileId(mapFileIds(fileIds)) + .setContext(contextService.createCallContext()) + .build(); + } + + private List<String> mapFileIds(List<FileId> fileIds) { + return fileIds.stream().map(fileIdMapper::toString).toList(); + } + + public void writeFileContent(FileId fileId, OutputStream out) { + var streamFuture = new CompletableFuture<Boolean>(); + var responseObserver = createDownloadBinaryFileObserver(streamFuture, out); + + asyncServiceStub.getBinaryFileContent(buildGrpcGetBinaryFileDataRequest(fileId), responseObserver); + + waitUntilFutureToComplete(streamFuture); + } + + void waitUntilFutureToComplete(CompletableFuture<Boolean> streamFuture) { + try { + streamFuture.get(10, TimeUnit.MINUTES); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TechnicalException(e.getMessage(), e); + } catch (ExecutionException | TimeoutException e) { + throw new TechnicalException(e.getMessage(), e); + } + } + + BinaryFileDownloadStreamObserver createDownloadBinaryFileObserver(CompletableFuture<Boolean> streamFuture, OutputStream out) { + return new BinaryFileDownloadStreamObserver(streamFuture, out); + } + + private GrpcGetBinaryFileDataRequest buildGrpcGetBinaryFileDataRequest(FileId fileId) { + return GrpcGetBinaryFileDataRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setFileId(fileIdMapper.toString(fileId)).build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java new file mode 100644 index 0000000000000000000000000000000000000000..8349b5fdc3331432655dbd8b38412891cd122691 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileService.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.io.OutputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import de.itvsh.goofy.common.file.OzgFile; + +@Service +@Validated +public class BinaryFileService { + + @Autowired + private BinaryFileRemoteService remoteService; + + public CompletableFuture<FileId> uploadFile(@Valid UploadBinaryFileRequest uploadBinaryFileRequest) { + return remoteService.uploadFile(uploadBinaryFileRequest); + } + + public Stream<OzgFile> getFiles(List<FileId> fileIds) { + return remoteService.getFiles(fileIds); + } + + public void writeFileContent(FileId fileId, OutputStream out) { + remoteService.writeFileContent(fileId, out); + } + + public OzgFile getFile(FileId fileId) { + return remoteService.getFile(fileId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserver.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..733d348bcde1149f6a20456046796fab881f7657 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserver.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.concurrent.CompletableFuture; + +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +class BinaryFileUploadStreamObserver implements ClientResponseObserver<GrpcUploadBinaryFileRequest, GrpcUploadBinaryFileResponse> { + + private final CompletableFuture<FileId> fileIdFuture; + private final ChunkedFileSender<GrpcUploadBinaryFileRequest> fileStreamer; + + @Getter + private String fileId; + + private ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver; + + @Override + public void beforeStart(ClientCallStreamObserver<GrpcUploadBinaryFileRequest> requestStream) { + this.requestObserver = requestStream; + requestObserver.setOnReadyHandler(() -> fileStreamer.sendChunkTo(requestObserver)); + } + @Override + public void onNext(GrpcUploadBinaryFileResponse response) { + fileId = response.getFileId(); + } + + @Override + public void onError(Throwable t) { + fileIdFuture.completeExceptionally(t); + } + + @Override + public void onCompleted() { + fileIdFuture.complete(FileId.from(fileId)); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSender.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSender.java new file mode 100644 index 0000000000000000000000000000000000000000..66044344b6cd624362f40db816b31ed3a0fd394b --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSender.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import io.grpc.stub.CallStreamObserver; +import lombok.AllArgsConstructor; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +@AllArgsConstructor +class ChunkedFileSender<T> { + + private final AtomicBoolean hasUploadFile = new AtomicBoolean(true); + private final InputStream uploadStream; + private final int chunkSize; + private final Function<byte[], T> buildChunkRequest; + private T requestMetadata; + + public void sendChunkTo(CallStreamObserver<T> streamObserver) { + if (hasUploadFile.get()) { + sendMetadata(streamObserver); + int size = sendNextChunk(streamObserver); + if (size < chunkSize) { + handleFileEndReached(streamObserver); + } + } + } + + private void sendMetadata(CallStreamObserver<T> streamObserver) { + if(requestMetadata != null) { + streamObserver.onNext(requestMetadata); + requestMetadata = null; + } + } + + private int sendNextChunk(CallStreamObserver<T> streamObserver) { + byte[] content = readFromStream(); + var size = content.length; + if (size > 0) { + streamObserver.onNext(buildChunkRequest.apply(content)); + } + return size; + } + + private byte[] readFromStream() { + try { + return uploadStream.readNBytes(chunkSize); + } catch (IOException e) { + throw new TechnicalException("Error on sending a single chunk", e); + } + } + + private void handleFileEndReached(CallStreamObserver<T> streamObserver) { + IOUtils.closeQuietly(uploadStream); + streamObserver.onCompleted(); + hasUploadFile.getAndSet(false); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandler.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..0dd43b57380ce87d98073d1b8b089ee2ded65b97 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandler.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.Objects; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +@Component +public class DownloadAuthenticationHandler { + + public boolean canDownloadFile(FileId fileId) { + return check(fileId, SecurityContextHolder.getContext().getAuthentication()); + } + + boolean check(FileId fileId, Authentication auth) { + if (auth instanceof UsernamePasswordAuthenticationToken userPasswordToken) { + GoofyUserWithFileId user = (GoofyUserWithFileId) userPasswordToken.getPrincipal(); + return Objects.nonNull(fileId) && fileId.equals(user.getFileId()) && auth.isAuthenticated(); + } + + return auth.isAuthenticated(); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DynamicViolationParameter.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DynamicViolationParameter.java new file mode 100644 index 0000000000000000000000000000000000000000..f4eca332a06c39cc2368ebeb2296852ec6c06816 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/DynamicViolationParameter.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class DynamicViolationParameter { + + @Builder.Default + private Map<String, Object> map = new HashMap<>(); +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileId.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileId.java new file mode 100644 index 0000000000000000000000000000000000000000..6ce48433f9ddc92118f355b2db182965c95b4c28 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileId.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.UUID; + +import de.itvsh.kop.common.datatype.StringBasedValue; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +public class FileId extends StringBasedValue { + + private static final long serialVersionUID = 1L; + + FileId(String fileId) { + super(fileId); + } + + public static FileId createNew() { + return from(UUID.randomUUID().toString()); + } + + public static FileId from(String fileId) { + return new FileId(fileId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileIdMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileIdMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..16895669c84d1f75cc9226a3d5bc6ece286728dc --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/FileIdMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import org.mapstruct.Mapper; + +@Mapper +public interface FileIdMapper { + + default FileId toFileId(String fileId) { + return FileId.from(fileId); + } + + default String toString(FileId fileId) { + return fileId.toString(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/GoofyUserWithFileId.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/GoofyUserWithFileId.java new file mode 100644 index 0000000000000000000000000000000000000000..f3156b59eacd6b043ba29455e99b8716c2368972 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/GoofyUserWithFileId.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import org.springframework.security.core.GrantedAuthority; + +import de.itvsh.goofy.common.user.UserProfile; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class GoofyUserWithFileId { + private UserProfile user; + + private FileId fileId; + + public Collection<GrantedAuthority> getAuthorities() { + return Objects.nonNull(user) ? user.getAuthorities() : List.of(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3297d3d57a116230f54dc74ce56dfd6e66c8375 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileRequest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.io.InputStream; + +import org.springframework.validation.annotation.Validated; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@Validated +@BinaryFileMaxSizeConstraint +class UploadBinaryFileRequest { + + private String vorgangId; + private String field; + + private String name; + private String contentType; + private long size; + private InputStream uploadStream; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..c7aebf3c23d222540a87baa284c9e5691f364af4 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidator.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.util.Map; +import java.util.Optional; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.unit.DataSize; +import org.springframework.util.unit.DataUnit; + +class UploadBinaryFileSizeValidator implements ConstraintValidator<BinaryFileMaxSizeConstraint, UploadBinaryFileRequest> { + + static final String DEFAULT_UNIT_STRING = "MB"; + static final DataUnit DEFAULT_UNIT = DataUnit.fromSuffix(DEFAULT_UNIT_STRING); + + static final int DEFAULT_MAX_SIZE = 40; + + private final DataSize defaultDataSize = DataSize.of(DEFAULT_MAX_SIZE, DEFAULT_UNIT); + + @Autowired + private BinaryFileProperties binaryFileProperties; + + @Override + public boolean isValid(UploadBinaryFileRequest uploadRequest, ConstraintValidatorContext context) { + var maxValidSize = getMaxValidSize(uploadRequest.getField()); + + setDynamicPayload(buildDynamicPayload(maxValidSize), context); + + return isFileSizeValid(DataSize.ofBytes(uploadRequest.getSize()), maxValidSize); + } + + private boolean isFileSizeValid(DataSize uploadFileSize, DataSize maxFileSize) { + return uploadFileSize.toBytes() <= maxFileSize.toBytes(); + } + + DataSize getMaxValidSize(String field) { + return Optional.ofNullable(binaryFileProperties.getMaxFileSize().get(field)).orElseGet(() -> defaultDataSize); + } + + private void setDynamicPayload(DynamicViolationParameter dynamicViolationParameter, ConstraintValidatorContext context) { + var unwrappedContext = getUnwrappedContext(HibernateConstraintValidatorContext.class, context); + unwrappedContext.withDynamicPayload(dynamicViolationParameter); + } + + <T> T getUnwrappedContext(Class<T> unwrapTo, ConstraintValidatorContext context) { + return context.unwrap(unwrapTo); + } + + private DynamicViolationParameter buildDynamicPayload(DataSize maxFileSize) { + return DynamicViolationParameter.builder().map(Map.of("max", maxFileSize.toMegabytes())).build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptor.java b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..df61fb0e7cfd2e4bb2627c8f03cb50ff4643be76 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptor.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import org.springframework.beans.factory.annotation.Autowired; + +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor; + +@GrpcGlobalClientInterceptor +public class CallContextAttachingInterceptor implements ClientInterceptor { + + @Autowired + private ContextService contextService; + + @Override + public <A, B> ClientCall<A, B> interceptCall(MethodDescriptor<A, B> method, CallOptions callOptions, Channel next) { + return new CallContextAttachingClientCall<>(next.newCall(method, callOptions)); + } + + final class CallContextAttachingClientCall<A, B> extends SimpleForwardingClientCall<A, B> { + + protected CallContextAttachingClientCall(ClientCall<A, B> delegate) { + super(delegate); + } + + @Override + public void start(Listener<B> responseListener, Metadata headers) { + headers.merge(contextService.buildCallContextMetadata()); + super.start(responseListener, headers); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..2bd03f5100ee719f2c99fef9afa463b011b1b3ae --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/CallContextMapper.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import org.mapstruct.Mapper; +import org.springframework.beans.factory.annotation.Autowired; + +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; + +@Mapper +public abstract class CallContextMapper { + + @Autowired + private ContextService contextService; + + public GrpcCallContext createCallContext() { + return contextService.createCallContext(); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/ContextService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/ContextService.java new file mode 100644 index 0000000000000000000000000000000000000000..11be31cbfbb0bee279081ffdc558d45118d00203 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/callcontext/ContextService.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import static de.itvsh.goofy.common.GrpcUtil.*; + +import java.util.Objects; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.RequestAttributes; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.command.GrpcUser; +import io.grpc.Metadata; +import lombok.extern.log4j.Log4j2; + +@Service +@Log4j2 +public class ContextService { + + static final String KEY_USER_ID = "USER_ID-bin"; + static final String KEY_USER_NAME = "USER_NAME-bin"; + static final String KEY_CLIENT_NAME = "CLIENT_NAME-bin"; + static final String KEY_REQUEST_ID = "REQUEST_ID-bin"; + static final String KEY_ACCESS_LIMITED_ORGAID = "ACCESS_LIMITED_TO_ORGANISATORISCHEEINHEITENID-bin"; + static final String KEY_ACCESS_LIMITED = "ACCESS_LIMITED-bin"; + + @Autowired + private ApplicationContext context; + + @Autowired + private CurrentUserService userService; + @Autowired(required = false) + private RequestAttributes reqAttributes; + + public GrpcCallContext createCallContext() { + return GrpcCallContext.newBuilder() + .setClient(context.getId()) + .setUser(createUser()) + .setRequestId(getRequestId()) + .build(); + } + + private GrpcUser createUser() { + var goofyUser = userService.getUser(); + + return GrpcUser.newBuilder() + .setId(goofyUser.getId().toString()) + .setName(goofyUser.getFullName()) + .build(); + } + + Metadata buildCallContextMetadata() { + var user = userService.getUser(); + var metadata = new Metadata(); + + Optional.ofNullable(user.getId()).map(id -> id.toString().getBytes()) + .ifPresentOrElse(bytes -> metadata.put(createKeyOf(KEY_USER_ID), bytes), () -> LOG.warn("Missing value 'userid'")); + Optional.ofNullable(user.getFullName()).map(String::getBytes) + .ifPresentOrElse(bytes -> metadata.put(createKeyOf(KEY_USER_NAME), bytes), () -> LOG.warn("Missing value 'fullname'")); + Optional.ofNullable(context.getId()).map(String::getBytes) + .ifPresentOrElse(bytes -> metadata.put(createKeyOf(KEY_CLIENT_NAME), bytes), () -> LOG.warn("Missing value 'client name'")); + user.getOrganisationseinheitIds().stream().map(String::getBytes) + .forEach(bytes -> metadata.put(createKeyOf(KEY_ACCESS_LIMITED_ORGAID), bytes)); + metadata.put(createKeyOf(KEY_ACCESS_LIMITED), isOrganisationEinheitenIdCheckNecessary()); + + return metadata; + } + + private byte[] isOrganisationEinheitenIdCheckNecessary() { + var hasRoleWithNoCheckNecessary = userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE) + || userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER); + + return Boolean.toString(!hasRoleWithNoCheckNecessary).getBytes(); + } + + String getRequestId() { + return Objects.isNull(reqAttributes) ? "- no request scope -" : reqAttributes.getRequestId(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..7b730e3c9954a5b5d24f597dc263c41eb8be7304 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteService.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.clientattribute; + +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.GoofyServerApplication; +import de.itvsh.ozg.pluto.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcUpdateClientAttributeRequest; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class ClientAttributeRemoteService { + @GrpcClient(GoofyServerApplication.GRPC_CLIENT) + private ClientAttributeServiceBlockingStub service; + + void resetPostfachNachricht(GrpcUpdateClientAttributeRequest request) { + service.update(request); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeService.java new file mode 100644 index 0000000000000000000000000000000000000000..31aa06ee96ab1eab2cb8127650a66567f446c0f8 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/clientattribute/ClientAttributeService.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.clientattribute; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcUpdateClientAttributeRequest; + +@Service +public class ClientAttributeService { + public static final String HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME = "hasNewPostfachNachricht"; + static final String ORIGINAL_CLIENT_NAME = "KopNachrichtenManager"; + @Autowired + private ClientAttributeRemoteService remoteService; + + public void resetPostfachNachricht(String vorgangId) { + + remoteService.resetPostfachNachricht(buildResetNewPostfachNachricht(vorgangId)); + } + + GrpcUpdateClientAttributeRequest buildResetNewPostfachNachricht(String vorgangId) { + return buildRequest(vorgangId, false, HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME); + } + + private GrpcUpdateClientAttributeRequest buildRequest(String vorgangId, boolean value, String attributeName) { + return GrpcUpdateClientAttributeRequest.newBuilder() + .setVorgangId(vorgangId) + .setAttribute( + GrpcClientAttribute.newBuilder().setValue( + GrpcClientAttributeValue.newBuilder().setBoolValue(value).build()) + .setClientName(ORIGINAL_CLIENT_NAME) + .setAttributeName(attributeName) + .build()) + .build(); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/Command.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/Command.java new file mode 100644 index 0000000000000000000000000000000000000000..40daa57c05fdba912585e1637538374de06dd0a5 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/Command.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.time.ZonedDateTime; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@Getter +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Command { + + @JsonIgnore + private String id; + private ZonedDateTime createdAt; + private ZonedDateTime finishedAt; + + @JsonIgnore + @LinkedUserProfileResource + private UserId createdBy; + private String createdByName; + + @JsonIgnore + private CommandStatus status; + private String errorMessage; + + @JsonIgnore + private String vorgangId; + @JsonIgnore + private String relationId; + private CommandOrder order; + + private RedirectRequest redirectRequest; + + private Map<String, ?> body; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBody.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBody.java new file mode 100644 index 0000000000000000000000000000000000000000..30bbda1d46cec622e188071b163285ee1f938a0b --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBody.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; + +import de.itvsh.goofy.kommentar.Kommentar; +import de.itvsh.goofy.postfach.PostfachMail; +import de.itvsh.goofy.vorgang.AssignUserCommandBody; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import de.itvsh.goofy.wiedervorlage.Wiedervorlage; + +@JsonSubTypes({ + @Type(value = PostfachMail.class, name = "SEND_POSTFACH_MAIL"), + @Type(value = PostfachMail.class, name = "SEND_POSTFACH_NACHRICHT"), + @Type(value = AssignUserCommandBody.class, name = "ASSIGN_USER"), + @Type(value = RedirectRequest.class, name = "REDIRECT_VORGANG"), + @Type(value = Wiedervorlage.class, name = "WIEDERVORLAGE"), + @Type(value = Kommentar.class, name = "KOMMENTAR") +}) +public interface CommandBody { + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBodyMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBodyMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..908b48b9e319741807eeed671f6504265a05d57c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandBodyMapper.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.apache.commons.beanutils.BeanMap; +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; + +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandBody; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandBodyField; + +@Mapper +public interface CommandBodyMapper { + + final Predicate<Entry<Object, Object>> IS_NOT_CLASS_VALUE = entry -> !StringUtils.equals("class", entry.getKey().toString()); + final Predicate<Entry<Object, Object>> IS_NOT_VERSION_VALUE = entry -> !StringUtils.equals("version", entry.getKey().toString()); + + static final String VORGANG_ID_PROPERTY = "vorgangId"; + static final String ITEM_NAME_PROPERTY = "itemName"; + static final String ITEM_PROPERTY = "item"; + + default GrpcCommandBody mapToBody(Map<String, String> bodyMap) { + if (Objects.isNull(bodyMap)) { + return GrpcCommandBody.getDefaultInstance(); + } + + var builder = GrpcCommandBody.newBuilder(); + + bodyMap.entrySet().stream() + .filter(entry -> Objects.nonNull(entry.getValue())) + .map(entry -> GrpcCommandBodyField.newBuilder().setName(entry.getKey()).setValue(entry.getValue()).build()) + .forEach(builder::addField); + + return builder.build(); + } + + default List<GrpcCommandBodyField> mapToBodyFields(CommandBody body) { + if (Objects.isNull(body)) { + return Collections.emptyList(); + } + + return mapToBodyFields(new BeanMap(body)); + } + + default List<GrpcCommandBodyField> mapToBodyFields(Map<Object, Object> bodyMap) { + if (Objects.isNull(bodyMap)) { + return Collections.emptyList(); + } + + return bodyMap.entrySet().stream() + .filter(IS_NOT_CLASS_VALUE) + .map(this::buildBodyField) + .toList(); + } + + default GrpcCommandBodyField buildBodyField(Entry<Object, Object> entry) { + var bodyFieldMapper = GrpcCommandBodyField.newBuilder().setName(entry.getKey().toString()); + + if (Objects.nonNull(entry.getValue())) { + bodyFieldMapper.setValue(getNullableStringValue(entry.getValue())); + } + return bodyFieldMapper.build(); + } + + default String getNullableStringValue(Object entryValue) { + return Optional.ofNullable(entryValue).map(Object::toString).orElse(null); + } + + default Map<String, String> map(GrpcCommandBody body) { + return body.getFieldList().stream().collect(Collectors.toMap(GrpcCommandBodyField::getName, GrpcCommandBodyField::getValue)); + } + + default Map<String, Object> mapToBodyMap(CreateCommand command, String itemName) { + return Map.of( + VORGANG_ID_PROPERTY, command.getVorgangId(), + ITEM_NAME_PROPERTY, itemName, + ITEM_PROPERTY, fromObjectToMap(command.getBody())); + } + + default Map<String, Object> fromObjectToMap(Object object) { + return new BeanMap(object).entrySet().stream() + .filter(IS_NOT_CLASS_VALUE) + .filter(IS_NOT_VERSION_VALUE) + .collect(HashMap::new, (map, entry) -> map.put(entry.getKey().toString(), checkEnumValue(entry.getValue())), Map::putAll); + } + + private Object checkEnumValue(Object entryValue) { + if (entryValue instanceof Enum<?> enumValue) { + return enumValue.name(); + } + return entryValue; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandController.java new file mode 100644 index 0000000000000000000000000000000000000000..304e4d4bac5844a27f08ab47c8a14ce895355c9c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandController.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.postfach.PostfachMail; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import lombok.Getter; + +@RestController +@RequestMapping(CommandController.COMMANDS_PATH) +public class CommandController { + + static final String COMMANDS_PATH = "/api/commands"; // NOSONAR + + static final String PARAM_PENDING = "pending"; + static final String PARAM_VORGANG_ID = "vorgangId"; + + @Autowired + private CommandService service; + @Autowired + private CommandModelAssembler modelAssembler; + + @GetMapping("{commandId}") + public EntityModel<Command> getById(@PathVariable String commandId) { + return modelAssembler.toModel(service.getById(commandId)); + } + + @PatchMapping("{commandId}") + public ResponseEntity<EntityModel<Command>> revoke(@PathVariable String commandId, @RequestBody StatusPatch patch) { + if (patch.getStatus() == CommandStatus.REVOKED) { + return ResponseEntity.ok(modelAssembler.toModel(service.revoke(commandId))); + } + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + @Getter + static class StatusPatch { + private CommandStatus status; + } + + @GetMapping(params = { PARAM_PENDING, PARAM_VORGANG_ID }) + public CollectionModel<EntityModel<Command>> getPendingCommands(@RequestParam boolean pending, @RequestParam String vorgangId) { + return modelAssembler.toCollectionModel(service.getPendingCommands(vorgangId)); + } + + public boolean existsPendingCommands(String relationId) { + return service.existsPendingCommands(relationId); + } + + @RestController + @RequestMapping(CommandByRelationController.COMMAND_BY_RELATION_PATH) + public static class CommandByRelationController { + + static final String COMMAND_BY_RELATION_PATH = "/api/vorgangs/{vorgangId}/relations/{relationId}/{relationVersion}/commands"; // NOSONAR + static final String PARAM_ORDER = "order"; + + @Autowired + private CommandService service; + @Autowired + private VorgangController vorgangController; + + @PostMapping + public ResponseEntity<Void> createCommand(@PathVariable String vorgangId, @PathVariable String relationId, + @PathVariable long relationVersion, @RequestBody CreateCommand command) { + command = command.toBuilder().vorgangId(vorgangId).relationId(relationId).build(); + + if (isSendPostfachMailOrder(command)) { + command = addPostfachIdToBody(command, vorgangId); + } + + var created = service.createCommand(command, relationVersion); + + return ResponseEntity.created(linkTo(CommandController.class).slash(created.getId()).toUri()).build(); + } + + private boolean isSendPostfachMailOrder(CreateCommand command) { + return command.getOrder() == CommandOrder.SEND_POSTFACH_NACHRICHT; + } + + CreateCommand addPostfachIdToBody(CreateCommand command, String vorgangId) { + var bodyWithPostfachId = ((PostfachMail) command.getBody()).toBuilder().postfachId(getPostfachId(vorgangId)).build(); + return command.toBuilder().body(bodyWithPostfachId).build(); + } + + private String getPostfachId(String vorgangId) { + return getVorgang(vorgangId).getEingang().getAntragsteller().getPostfachId(); + } + + private VorgangWithEingang getVorgang(String vorgangId) { + return vorgangController.getVorgang(vorgangId); + } + + public Command createCommand(CreateCommand command) { + return service.createCommand(command); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a655d8ddcc6a68f666385f5578e29c7f940fb500 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandMapper.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.Map; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.ValueMapping; +import org.springframework.beans.factory.annotation.Autowired; + +import de.itvsh.goofy.common.TimeMapper; +import de.itvsh.goofy.common.callcontext.CallContextMapper; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.grpc.command.GrpcRedirectRequest; + +@Mapper(uses = { CallContextMapper.class, TimeMapper.class, CommandBodyMapper.class, GrpcObjectMapper.class, UserIdMapper.class }) +public abstract class CommandMapper { + + @Autowired + private GrpcObjectMapper objectMapper; + @Autowired + private CommandBodyMapper bodyMapper; + + @ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL) + @ValueMapping(source = "UNDEFINED", target = MappingConstants.NULL) + @Mapping(target = "order", expression = "java(mapOrder(grpcCommand))") + @Mapping(target = "body", expression = "java(mapBody(grpcCommand))") + abstract Command toCommand(GrpcCommand grpcCommand); + + RedirectRequest mapRedirectRequest(GrpcRedirectRequest request) { + var builder = RedirectRequest.builder().email(request.getEmail()); + if (StringUtils.isNotEmpty(request.getPassword())) { + builder.password(request.getPassword().toCharArray()); + } + return builder.build(); + } + + Map<String, ?> mapBody(GrpcCommand command) { + var bodyObject = objectMapper.mapFromGrpc(command.getBodyObj()); + + if (MapUtils.isNotEmpty(bodyObject)) { + return bodyObject; + } else { + return bodyMapper.map(command.getBody()); + } + } + + CommandOrder mapOrder(GrpcCommand command) { + if (StringUtils.isNotBlank(command.getOrderString())) { + return CommandOrder.valueOf(command.getOrderString()); + } else { + return CommandOrder.valueOf(command.getOrder().name()); + } + } + + String mapStringtoNull(String in) { + return StringUtils.trimToNull(in); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..db21be840129e5d1907caf0de93d14b9ce438fa2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandModelAssembler.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.errorhandling.FunctionalException; +import de.itvsh.goofy.kommentar.KommentarController; +import de.itvsh.goofy.postfach.PostfachMailController; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.forwarding.ForwardingController; +import de.itvsh.goofy.wiedervorlage.WiedervorlageController; + +@Component +class CommandModelAssembler implements RepresentationModelAssembler<Command, EntityModel<Command>> { + + static final LinkRelation REL_EFFECTED_RESOURCE = LinkRelation.of("effected_resource"); + static final LinkRelation REL_REVOKE = LinkRelation.of("revoke"); + static final LinkRelation REL_UPDATE = LinkRelation.of("update"); + + private static final Predicate<Command> IS_DONE = command -> command.getStatus() == CommandStatus.FINISHED + || command.getStatus() == CommandStatus.REVOKED || command.getStatus() == CommandStatus.ERROR; + + private static final Predicate<Command> IS_PENDING = command -> command.getStatus() == CommandStatus.PENDING + || command.getStatus() == CommandStatus.REVOKE_PENDING; + + @Override + public EntityModel<Command> toModel(Command entity) { + WebMvcLinkBuilder selfLinkBuilder = linkTo(CommandController.class).slash(entity.getId()); + Link revokeLink = linkTo(methodOn(CommandController.class).revoke(entity.getId(), null)).withRel(REL_REVOKE); + + var resultModel = EntityModel.of(entity, selfLinkBuilder.withSelfRel()); + resultModel = addIf(IS_DONE, resultModel, effectedResourceLinkByOrder(entity)); + resultModel = addIf(IS_PENDING, resultModel, selfLinkBuilder.withRel(REL_UPDATE)); + resultModel = addIf(command -> command.getOrder().isRevokeable(), resultModel, revokeLink); + return resultModel; + } + + Link effectedResourceLinkByOrder(Command entity) { + switch (entity.getOrder().getType()) { + case VORGANG: + return linkTo(VorgangController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + case WIEDERVORLAGE: + return linkTo(WiedervorlageController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + case KOMMENTAR: + return linkTo(KommentarController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + case FORWARDING: + return linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); + case POSTFACH: + return linkTo(methodOn(PostfachMailController.class).getAll(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); + default: + throw new FunctionalException(() -> "command_order_unkown"); + } + } + + private EntityModel<Command> addIf(Predicate<Command> condition, EntityModel<Command> model, Link link) { + return Optional.ofNullable(model.getContent()).filter(condition).map(command -> model.add(link)).orElse(model); + } + + public CollectionModel<EntityModel<Command>> toCollectionModel(Stream<Command> entities) { + return CollectionModel.of(entities.map(this::toModel).collect(Collectors.toList()), linkTo(CommandController.class).withSelfRel()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java new file mode 100644 index 0000000000000000000000000000000000000000..594a553a06a28fd02f36258d02c1a133e883517e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandOrder.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public enum CommandOrder { + VORGANG_ANNEHMEN(true, Type.VORGANG), + VORGANG_VERWERFEN(true, Type.VORGANG), + VORGANG_ZURUECKHOLEN(true, Type.VORGANG), + VORGANG_BEARBEITEN(true, Type.VORGANG), + VORGANG_BESCHEIDEN(true, Type.VORGANG), + VORGANG_ZURUECKSTELLEN(true, Type.VORGANG), + VORGANG_ABSCHLIESSEN(true, Type.VORGANG), + VORGANG_WIEDEREROEFFNEN(true, Type.VORGANG), + + ASSIGN_USER(false, Type.VORGANG), + + REDIRECT_VORGANG(false, Type.FORWARDING), + FORWARD_SUCCESSFULL(false, Type.FORWARDING), + FORWARD_FAILED(false, Type.FORWARDING), + + CREATE_KOMMENTAR(false, Type.KOMMENTAR), + EDIT_KOMMENTAR(false, Type.KOMMENTAR), + + CREATE_WIEDERVORLAGE(false, Type.WIEDERVORLAGE), + EDIT_WIEDERVORLAGE(false, Type.WIEDERVORLAGE), + WIEDERVORLAGE_ERLEDIGEN(false, Type.WIEDERVORLAGE), + WIEDERVORLAGE_WIEDEREROEFFNEN(false, Type.WIEDERVORLAGE), + + @Deprecated + SEND_POSTFACH_MAIL(false, Type.POSTFACH), + SEND_POSTFACH_NACHRICHT(false, Type.POSTFACH), + RESEND_POSTFACH_MAIL(false, Type.POSTFACH), + RECEIVE_POSTFACH_NACHRICHT(false, Type.POSTFACH), + + CREATE_ATTACHED_ITEM(false, Type.VORGANG), + UPDATE_ATTACHED_ITEM(false, Type.VORGANG), + PATCH_ATTACHED_ITEM(false, Type.VORGANG); + + enum Type { + VORGANG, WIEDERVORLAGE, KOMMENTAR, FORWARDING, POSTFACH + } + + private final boolean revokeable; + @Getter(value = AccessLevel.PROTECTED) + private final Type type; + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..6ed320f930829d0a198bb89a527be4af583d99f0 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandRemoteService.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.command.GrpcCreateCommandRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcExistsPendingCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcFindCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcGetPendingCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcOrder; +import de.itvsh.ozg.pluto.grpc.command.GrpcRedirectRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcRevokeCommandRequest; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +public class CommandRemoteService { + + @GrpcClient("pluto") + private CommandServiceBlockingStub commandServiceStub; + @Autowired + private ContextService contextService; + @Autowired + private CommandMapper mapper; + @Autowired + private CommandBodyMapper bodyMapper; + @Autowired + private GrpcObjectMapper objectMapper; + + public Command createCommand(CreateCommand command) { + var response = commandServiceStub.createCommand(buildCreateCommandRequest(command)); + + return mapper.toCommand(response.getCommand()); + } + + GrpcCreateCommandRequest buildCreateCommandRequest(CreateCommand command) { + var requestBuilder = GrpcCreateCommandRequest.newBuilder() + .setCallContext(contextService.createCallContext()) + .setVorgangId(command.getVorgangId()) + .setRelationId(command.getRelationId()) + .setRelationVersion(command.getRelationVersion()) + .setOrderString(command.getOrder().name()) + .addAllBody(bodyMapper.mapToBodyFields(command.getBody())) + .setBodyObj(objectMapper.fromMap(bodyMapper.fromObjectToMap(command.getBody()))); + + Optional.ofNullable(command.getRedirectRequest()).map(this::buildForwardRequest).ifPresent(requestBuilder::setRedirectRequest); + + return requestBuilder.build(); + } + + GrpcRedirectRequest buildForwardRequest(RedirectRequest forwardRequest) { + var builder = GrpcRedirectRequest.newBuilder().setEmail(forwardRequest.getEmail()); + if (ArrayUtils.isNotEmpty(forwardRequest.getPassword())) { + builder.setPassword(String.valueOf(forwardRequest.getPassword())); + } + return builder.build(); + } + + public Command revokeCommand(String commandId) { + var response = commandServiceStub.revokeCommand(createRevokeRequest(commandId)); + + return mapper.toCommand(response.getCommand()); + } + + GrpcRevokeCommandRequest createRevokeRequest(String commandId) { + return GrpcRevokeCommandRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setId(commandId) + .build(); + } + + public Command getCommand(String commandId) { + var response = commandServiceStub.getCommand(createGetCommandRequest(commandId)); + + return mapper.toCommand(response); + } + + GrpcGetCommandRequest createGetCommandRequest(String commandId) { + return GrpcGetCommandRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setId(commandId) + .build(); + } + + public boolean existsPendingCommands(String relationId) { + var response = commandServiceStub.existsPendingCommands(buildGrpcExistsPendingCommandsRequest(relationId)); + + return response.getExistsPendingCommands(); + } + + private GrpcExistsPendingCommandsRequest buildGrpcExistsPendingCommandsRequest(String relationId) { + return GrpcExistsPendingCommandsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setVorgangId(relationId) + .build(); + } + + public Stream<Command> getPendingCommands(String vorgangId) { + var response = commandServiceStub.getPendingCommands(buildGrpcGetPendingCommandsRequest(vorgangId)); + + return response.getCommandList().stream().map(mapper::toCommand); + } + + private GrpcGetPendingCommandsRequest buildGrpcGetPendingCommandsRequest(String vorgangId) { + return GrpcGetPendingCommandsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setVorgangId(vorgangId) + .build(); + } + + public Stream<Command> findCommands(String vorgangId, Optional<CommandStatus> status, Optional<CommandOrder> order) { + var response = commandServiceStub.findCommands(buildFindCommandsRequest(vorgangId, status, order)); + + return response.getCommandList().stream().map(mapper::toCommand); + } + + private GrpcFindCommandsRequest buildFindCommandsRequest(String vorgangId, Optional<CommandStatus> status, Optional<CommandOrder> order) { + var builder = GrpcFindCommandsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setVorgangId(vorgangId); + + status.map(CommandStatus::name).ifPresent(builder::addStatus); + order.map(commandOrder -> GrpcOrder.valueOf(commandOrder.name())).ifPresent(builder::setOrder); + + return builder.build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandService.java new file mode 100644 index 0000000000000000000000000000000000000000..a55a1ff0b5d7d6af7907651fa535481189f8444a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandService.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.Optional; +import java.util.stream.Stream; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import lombok.NonNull; + +@Validated +@Service +public class CommandService { + + static final long NO_RELATION_VERSION = -1; + + @Autowired + private CommandRemoteService remoteService; + + public Command createCommand(CreateCommand command) { + command = command.toBuilder().relationVersion(NO_RELATION_VERSION).build(); + + return remoteService.createCommand(command); + } + + public Command createCommand(@Valid CreateCommand command, long relationVersion) { + command = command.toBuilder().relationVersion(relationVersion).build(); + + return remoteService.createCommand(command); + } + + public Command getById(String commandId) { + return remoteService.getCommand(commandId); + } + + public Command revoke(String commandId) { + return remoteService.revokeCommand(commandId); + } + + public boolean existsPendingCommands(String relationId) { + return remoteService.existsPendingCommands(relationId); + } + + public Stream<Command> getPendingCommands(String vorgangId) { + return remoteService.getPendingCommands(vorgangId); + } + + public Stream<Command> findFinishedCommands(@NonNull String vorgangId) { + return remoteService.findCommands(vorgangId, Optional.of(CommandStatus.FINISHED), Optional.empty()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandStatus.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..7a8c8e0e35b4b48c07bf600a6c50f819b84b0de9 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CommandStatus.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +public enum CommandStatus { + PENDING, FINISHED, ERROR, REVOKE_PENDING, REVOKED; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/command/CreateCommand.java b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CreateCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..762515619039551e24a3a470fd6377c160beedde --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/command/CreateCommand.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import javax.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder(toBuilder = true) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CreateCommand { + + @JsonIgnore + private UserId createdBy; + @JsonIgnore + private CommandStatus status; + + private String vorgangId; + private String relationId; + private long relationVersion; + private CommandOrder order; + + @Valid + private RedirectRequest redirectRequest; + + @Valid + @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "order") + private CommandBody body; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilter.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..fda3e1a7381334201313d22ff7a4b6e68cd9d496 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilter.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import de.itvsh.kop.common.errorhandling.TechnicalException; + +@Component +@Order(0) +public class DownloadTokenAuthenticationFilter extends OncePerRequestFilter { + @Autowired + private DownloadTokenService downloadTokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + try { + downloadTokenService.handleToken(request, getDownloadToken(request)); + } catch (TechnicalException e) { + response.setStatus(HttpStatus.SC_UNAUTHORIZED); + return; + } + + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + return StringUtils.isEmpty(getDownloadToken(request)); + } + + private String getDownloadToken(HttpServletRequest request) { + return request.getParameter(DownloadTokenController.PARAM_TOKEN); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenController.java new file mode 100644 index 0000000000000000000000000000000000000000..e9d427ce4f4429783bf46d767f84eaf4f05dd9e1 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenController.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.net.URI; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.binaryfile.FileId; + +@RestController +@RequestMapping(DownloadTokenController.DOWNLOAD_TOKEN_PATH) +public class DownloadTokenController { + + public static final String DOWNLOAD_TOKEN_PATH = "/api/downloadtoken"; // NOSONAR + + public static final String PARAM_TOKEN = "token"; + + @Autowired + private DownloadTokenService tokenService; + + @PostMapping + public ResponseEntity<Void> downloadToken(@RequestBody DownloadTokenRequest request) { + return ResponseEntity.created(buildUri(request.getFileId())).build(); + } + + private URI buildUri(FileId fileId) { + return linkTo(DownloadTokenController.class).toUriComponentsBuilder().queryParam(PARAM_TOKEN, tokenService.createToken(fileId)) + .build() + .toUri(); + } + + @GetMapping + public ResponseEntity<DownloadTokenResponse> getToken(@RequestParam String token) { + return ResponseEntity.ok(DownloadTokenResponse.builder().token(token).build()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenProperties.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..a124a43e3d8973189726c64a1bfd800510685785 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenProperties.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import javax.validation.constraints.NotNull; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Validated +@Configuration +@ConfigurationProperties(DownloadTokenProperties.PREFIX) +public class DownloadTokenProperties { + + public static final String PREFIX = "kop.auth.token"; + + @NotNull + private String secret; + @NotNull + private int validity; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenRequest.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..40aad859635de8387ec8a1b617f2133e5c68f438 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenRequest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import de.itvsh.goofy.common.LinkedResource; +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.binaryfile.FileId; +import lombok.Getter; + +@Getter +class DownloadTokenRequest { + + @LinkedResource(controllerClass = BinaryFileController.class) + private FileId fileId; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenResponse.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..e13db1135c812f2280b541f1aa764dbc90fffc8a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenResponse.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +class DownloadTokenResponse { + + private String token; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenService.java new file mode 100644 index 0000000000000000000000000000000000000000..e4b234bac54a3eb29c511d494394f0f1c9eeb0c0 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenService.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static de.itvsh.goofy.JwtTokenUtil.*; + +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Service; + +import com.auth0.jwt.exceptions.JWTVerificationException; + +import de.itvsh.goofy.JwtTokenUtil; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.binaryfile.GoofyUserWithFileId; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import io.jsonwebtoken.Claims; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Service +class DownloadTokenService { + + @Autowired + private CurrentUserService userService; + + @Autowired + private JwtTokenUtil jwtTokenUtil; + + public String createToken(FileId fileId) { + var user = userService.getUser(); + return jwtTokenUtil.generateToken(fileId, user); + } + + public void handleToken(HttpServletRequest request, String token) { + if (StringUtils.isNotBlank(token)) { + try { + jwtTokenUtil.verifyToken(token); + + SecurityContextHolder.getContext().setAuthentication(buildAuthentication(request, token)); + } catch (JWTVerificationException e) { + LOG.error("JwtVerficationException in DownloadTokenService", e); + throw new TechnicalException("download token not valid", e); + } + } + } + + private Authentication buildAuthentication(HttpServletRequest request, String token) { + var user = getUserFromToken(token); + + var authenticationToken = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + return authenticationToken; + } + + GoofyUserWithFileId getUserFromToken(String token) { + Optional<Claims> claimsOptional = jwtTokenUtil.getAllClaimsFromToken(token); + var downloadUserBuilder = GoofyUserWithFileId.builder(); + claimsOptional.ifPresent(claims -> downloadUserBuilder.user( + UserProfile.builder() + .id(UserId.from(claims.get(USERID_CLAIM, String.class))) + .firstName(claims.get(FIRSTNAME_CLAIM, String.class)) + .lastName(claims.get(LASTNAME_CLAIM, String.class)) + .authorities(jwtTokenUtil.getRolesFromToken(token)) + .organisationseinheitIds(jwtTokenUtil.getOrganisationseinheitIdsFromToken(token)).build()) + .fileId(FileId.from(claims.get(FILEID_CLAIM, String.class)))); + + return downloadUserBuilder.build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ApiError.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ApiError.java new file mode 100644 index 0000000000000000000000000000000000000000..05ead44be65ccd8b7fb79f9d00d2d72cf705bfd7 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ApiError.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +class ApiError { + + @Singular + private List<Issue> issues; + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java new file mode 100644 index 0000000000000000000000000000000000000000..bc6076da1206786609275e87069265e5bbef0dca --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ExceptionController.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Path; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.engine.HibernateConstraintViolation; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import de.itvsh.goofy.common.binaryfile.DynamicViolationParameter; +import de.itvsh.kop.common.errorhandling.ExceptionUtil; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import lombok.extern.log4j.Log4j2; + +@ControllerAdvice +@Log4j2 +@Order(99) +public class ExceptionController { + + private static final Set<String> IGNORABLE_CONSTRAINT_VIOLATION_ATTRIBUTES = new HashSet<>(Arrays.asList("groups", "payload", "message")); + + static final String RUNTIME_MESSAGE_CODE = "generale.server_error"; + static final String RESOURCE_NOT_FOUNT_MESSAGE_CODE = "resource.not_found"; + static final String ACCESS_DENIED_MESSAGE_CODE = "generale.access_denied"; + static final String SERVICE_UNAVAILABLE_MESSAGE_CODE = "generale.service_unavailable"; + + @ExceptionHandler(FunctionalException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ApiError handleFunctionalException(FunctionalException e) { + LOG.error("BadRequest", e); + return ApiError.builder().issue(buildIssueForFunctionalException(e)).build(); + } + + private Issue buildIssueForFunctionalException(FunctionalException e) { + return Issue.builder().message(e.getMessage()).messageCode(e.getErrorCode()).exceptionId(e.getExceptionId()).build(); + } + + @ExceptionHandler(AccessDeniedException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + @ResponseBody + public ApiError handleAccessDeniedException(AccessDeniedException e) { + var exceptionId = createExceptionId(); + var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); + LOG.error("Access Denied Exception", messageWithExceptionId); + + return ApiError.builder().issue(buildIssueForAccessDeniedException(messageWithExceptionId, exceptionId)).build(); + } + + private Issue buildIssueForAccessDeniedException(String message, String exceptionId) { + return Issue.builder().messageCode(ACCESS_DENIED_MESSAGE_CODE).message(message).exceptionId(exceptionId).build(); + } + + @ExceptionHandler(ResourceNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + @ResponseBody + public ApiError handleResourceNotFoundException(ResourceNotFoundException e) { + LOG.warn("Resource not found: {}", e.getMessage()); + return ApiError.builder().issue(buildIssueForResourceNotFoundException(e)).build(); + } + + private Issue buildIssueForResourceNotFoundException(ResourceNotFoundException e) { + return Issue.builder().message(e.getMessage()).messageCode(RESOURCE_NOT_FOUNT_MESSAGE_CODE).exceptionId(e.getExceptionId()).build(); + } + + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ResponseBody + public ApiError handleConstraintViolationException(ConstraintViolationException e) { + var exceptionId = createExceptionId(); + var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); + LOG.warn("Validation Exception: {}", messageWithExceptionId); + return ApiError.builder().issues(buildIssues(e, exceptionId)).build(); + } + + private List<Issue> buildIssues(ConstraintViolationException e, String exceptionId) { + return e.getConstraintViolations().stream() + .map(violation -> buildIssue(violation, exceptionId)) + .collect(Collectors.toList()); + } + + private Issue buildIssue(ConstraintViolation<?> violation, String exceptionId) { + return Issue.builder()// + .field(buildFieldPath(violation.getPropertyPath()))// + .messageCode(violation.getMessageTemplate().replace("{", "").replace("}", ""))// + .message(violation.getMessage())// + .parameters(buildParameters(violation).collect(Collectors.toList())) + .exceptionId(exceptionId) + .build(); + } + + private String buildFieldPath(Path propertyPath) { + return propertyPath.toString().substring(propertyPath.toString().indexOf('.') + 1); + } + + Stream<IssueParam> buildParameters(ConstraintViolation<?> violation) { + var dynamicPayload = getDynamicPayload(violation); + return Optional.ofNullable(violation.getConstraintDescriptor()) + .map(ConstraintDescriptor::getAttributes) + .map(descr -> descr.entrySet().stream() + .filter(entry -> !IGNORABLE_CONSTRAINT_VIOLATION_ATTRIBUTES.contains(entry.getKey())) + .map(entryMap -> buildIssueParam(entryMap, dynamicPayload))) + .orElse(Stream.empty()); + } + + private IssueParam buildIssueParam(Entry<String, Object> entry, Optional<DynamicViolationParameter> dynamicValues) { + return IssueParam.builder().name(entry.getKey()).value(getValue(entry, dynamicValues)).build(); + } + + private String getValue(Entry<String, Object> entry, Optional<DynamicViolationParameter> dynamicValues) { + return dynamicValues + .map(DynamicViolationParameter::getMap) + .map(map -> map.get(entry.getKey())) + .filter(Objects::nonNull) + .map(String::valueOf) + .orElse(String.valueOf(entry.getValue())); + } + + private Optional<DynamicViolationParameter> getDynamicPayload(ConstraintViolation<?> violation) { + HibernateConstraintViolation<?> hibernateViolation = violation.unwrap(HibernateConstraintViolation.class); + return Optional.ofNullable(hibernateViolation.getDynamicPayload(DynamicViolationParameter.class)); + } + + @ExceptionHandler(TechnicalException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public ApiError handleTechnicalException(TechnicalException e) { + LOG.error("TechnicalException on Request", e); + return buildRuntimeApiError(e.getMessage(), e.getExceptionId()); + } + + @ExceptionHandler(RuntimeException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public ApiError handleRuntimeException(RuntimeException e) { + var exceptionId = createExceptionId(); + var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); + LOG.error("RuntimeException on Request", messageWithExceptionId); + LOG.error("RuntimeException on Request", e); + return buildRuntimeApiError(messageWithExceptionId, exceptionId); + } + + String createExceptionId() { + return UUID.randomUUID().toString(); + } + + private ApiError buildRuntimeApiError(String message, String exceptionId) { + return ApiError.builder().issue(createIssueForRuntimeException(message, exceptionId)).build(); + } + + private Issue createIssueForRuntimeException(String message, String exceptionId) { + return Issue.builder().messageCode(RUNTIME_MESSAGE_CODE).message(message).exceptionId(exceptionId).build(); + } + + @ExceptionHandler(ServiceUnavailableException.class) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + @ResponseBody + public ApiError handleServiceUnavailableException(ServiceUnavailableException exception) { + return buildServiceUnavailableApiError(exception); + } + + private ApiError buildServiceUnavailableApiError(ServiceUnavailableException exception) { + return ApiError.builder().issue(createIssueForServiceUnavailableException(exception)).build(); + } + + private Issue createIssueForServiceUnavailableException(ServiceUnavailableException exception) { + return Issue.builder().messageCode(exception.getMessageCode()).message(exception.getMessage()).build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/FunctionalException.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/FunctionalException.java new file mode 100644 index 0000000000000000000000000000000000000000..49c826c0ad247cb9bb7a5c3b8103ace31a140f78 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/FunctionalException.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import java.util.UUID; + +import de.itvsh.kop.common.errorhandling.ExceptionUtil; +import de.itvsh.kop.common.errorhandling.FunctionalErrorCode; +import de.itvsh.kop.common.errorhandling.IdentifiableException; + +public class FunctionalException extends RuntimeException implements IdentifiableException { + + private static final long serialVersionUID = 1L; + + private final FunctionalErrorCode errorCode; + private final String exceptionId; + + public FunctionalException(FunctionalErrorCode errorCode) { + super("Functional error: " + errorCode.getErrorCode()); + + this.errorCode = errorCode; + this.exceptionId = UUID.randomUUID().toString(); + } + + public String getErrorCode() { + return errorCode.getErrorCode(); + } + + @Override + public String getMessage() { + return ExceptionUtil.formatMessageWithExceptionId(super.getMessage(), exceptionId); + } + + @Override + public String getExceptionId() { + return exceptionId; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionController.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionController.java new file mode 100644 index 0000000000000000000000000000000000000000..97d5fdd33c9a8b93c3e565094f131aa91dd3ebe2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionController.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import de.itvsh.kop.common.errorhandling.ExceptionUtil; +import de.itvsh.kop.common.errorhandling.FunctionalErrorCode; +import io.grpc.Metadata; +import io.grpc.Metadata.Key; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import lombok.extern.log4j.Log4j2; + +@ControllerAdvice +@Log4j2 +@Order(98) +public class GrpcExceptionController { + + static final FunctionalErrorCode GRPC_NOT_FOUND_ERROR_CODE_KEY = () -> ExceptionController.RESOURCE_NOT_FOUNT_MESSAGE_CODE; + static final FunctionalErrorCode GRPC_INTERNAL_ERROR_CODE_KEY = () -> ExceptionController.RUNTIME_MESSAGE_CODE; + static final FunctionalErrorCode GRPC_PERMISSION_DENIED_CODE_KEY = () -> ExceptionController.ACCESS_DENIED_MESSAGE_CODE; + + static final String KEY_ERROR_CODE = "ERROR_CODE"; + static final String KEY_EXCEPTION_ID = "EXCEPTION_ID"; + + private final Predicate<String> ignoreKey = key -> !isErrorCode(key) && !isExceptionId(key); + + private boolean isErrorCode(String metadataKey) { + return isParamKeyEquals(metadataKey, KEY_ERROR_CODE); + } + + private boolean isExceptionId(String metadataKey) { + return isParamKeyEquals(metadataKey, KEY_EXCEPTION_ID); + } + + private boolean isParamKeyEquals(String metaDataKey, String key) { + return createMetadataKey(metaDataKey).equals(createMetadataKey(key)); + } + + @ExceptionHandler(StatusRuntimeException.class) + public ResponseEntity<ApiError> handleStatusRuntimeException(StatusRuntimeException e) { + if (isNotFoundStatusCode(e)) { + return buildNotFoundErrorResponse(e); + } else if (isInternalStatusCode(e)) { + return buildInternalErrorResponse(e); + } else if (isPermissionDeniedCode(e)) { + return buildAccessDeniedErrorResponse(e); + } + + return buildInternalUnavailableErrorResponse(e); + } + + private boolean isNotFoundStatusCode(StatusRuntimeException e) { + return e.getStatus().getCode() == Status.NOT_FOUND.getCode(); + } + + private ResponseEntity<ApiError> buildNotFoundErrorResponse(StatusRuntimeException e) { + LOG.error("Grpc not found exception: {}", e.getMessage()); + + Issue.IssueBuilder issueBuilder = Issue.builder().message(e.getMessage()).messageCode(GRPC_NOT_FOUND_ERROR_CODE_KEY.getErrorCode()); + addExceptionId(issueBuilder, e); + + return new ResponseEntity<>(buildApiErrorWithIssue(issueBuilder.build()), HttpStatus.NOT_FOUND); + } + + private ResponseEntity<ApiError> buildInternalUnavailableErrorResponse(StatusRuntimeException e) { + if (hasExceptionId(e)) { + return new ResponseEntity<>(buildInternalUnavailableApiError(e, getExceptionId(e)), HttpStatus.SERVICE_UNAVAILABLE); + } + + var exceptionId = createExceptionId(); + var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); + LOG.error("Grpc service unavailable: {}", messageWithExceptionId); + + return new ResponseEntity<>(buildInternalUnavailableApiError(e, exceptionId), HttpStatus.SERVICE_UNAVAILABLE); + } + + String createExceptionId() { + return UUID.randomUUID().toString(); + } + + private ApiError buildInternalUnavailableApiError(StatusRuntimeException e, String exceptionId) { + return ApiError.builder().issue(Issue.builder().message(e.getMessage()).messageCode(getMessageCode(e)).exceptionId(exceptionId).build()) + .build(); + } + + private ResponseEntity<ApiError> buildInternalErrorResponse(StatusRuntimeException e) { + LOG.error("Grpc internal server error: " + e.getMessage(), e); + + return new ResponseEntity<>(buildInternalApiError(e), HttpStatus.INTERNAL_SERVER_ERROR); + } + + private boolean isInternalStatusCode(StatusRuntimeException e) { + return e.getStatus().getCode() == Status.INTERNAL.getCode(); + } + + private ApiError buildInternalApiError(StatusRuntimeException e) { + Issue.IssueBuilder issueBuilder = Issue.builder().message(e.getMessage()).messageCode(getMessageCode(e)); + + addParameter(issueBuilder, e); + addExceptionId(issueBuilder, e); + + return buildApiErrorWithIssue(issueBuilder.build()); + } + + private String getMessageCode(StatusRuntimeException e) { + return hasErrorCode(e) ? getErrorCode(e) : GRPC_INTERNAL_ERROR_CODE_KEY.getErrorCode(); + } + + private boolean hasErrorCode(StatusRuntimeException e) { + return hasParameter(e) && StringUtils.isNotEmpty(getErrorCode(e)); + } + + private ApiError buildApiErrorWithIssue(Issue issue) { + return ApiError.builder().issue(issue).build(); + } + + private boolean isPermissionDeniedCode(StatusRuntimeException e) { + return e.getStatus().getCode() == Status.PERMISSION_DENIED.getCode(); + } + + private ResponseEntity<ApiError> buildAccessDeniedErrorResponse(StatusRuntimeException e) { + var exceptionId = createExceptionId(); + var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); + LOG.error("Grpc permission denied error: " + messageWithExceptionId); + + return new ResponseEntity<>(buildAccessDeniedApiError(e, exceptionId), HttpStatus.FORBIDDEN); + } + + private ApiError buildAccessDeniedApiError(StatusRuntimeException e, String exceptionId) { + return ApiError.builder() + .issue(Issue.builder().message(e.getMessage()).messageCode(GRPC_PERMISSION_DENIED_CODE_KEY.getErrorCode()).exceptionId(exceptionId) + .build()) + .build(); + } + + void addParameter(Issue.IssueBuilder builder, StatusRuntimeException e) { + if (hasParameter(e)) { + builder.parameters(mapToIssueParams(e.getTrailers())); + } + } + + List<IssueParam> mapToIssueParams(Metadata metaData) { + return metaData.keys().stream() + .filter(ignoreKey) + .map(key -> buildIssueParam(metaData, key)) + .collect(Collectors.toList()); + } + + private IssueParam buildIssueParam(Metadata metaData, String key) { + return IssueParam.builder().name(key).value(metaData.get(createMetadataKey(key))).build(); + } + + void addExceptionId(Issue.IssueBuilder builder, StatusRuntimeException e) { + if (hasExceptionId(e)) { + builder.exceptionId(getExceptionId(e)); + } + } + + private boolean hasExceptionId(StatusRuntimeException e) { + return hasParameter(e) && StringUtils.isNotEmpty(getExceptionId(e)); + } + + private boolean hasParameter(StatusRuntimeException e) { + return Objects.nonNull(e.getTrailers()); + } + + private String getExceptionId(StatusRuntimeException e) { + return getParameter(e, KEY_EXCEPTION_ID); + } + + private String getErrorCode(StatusRuntimeException e) { + return getParameter(e, KEY_ERROR_CODE); + } + + private String getParameter(StatusRuntimeException e, String metadataKey) { + return e.getTrailers().get(createMetadataKey(metadataKey)); + } + + private Key<String> createMetadataKey(String key) { + return Key.of(key, Metadata.ASCII_STRING_MARSHALLER); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/Issue.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/Issue.java new file mode 100644 index 0000000000000000000000000000000000000000..867bbe07b5dc393fe4037cf3b9b52aebc883cfcf --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/Issue.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Singular; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Issue { + + private String field; + private String messageCode; + private String message; + private String exceptionId; + + @Singular + private List<IssueParam> parameters; + +} + +@Builder +@Getter +class IssueParam { + private String name; + private String value; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/MessageCode.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/MessageCode.java new file mode 100644 index 0000000000000000000000000000000000000000..c4875de80dd5189737a8896f0e28e52ac3f98349 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/MessageCode.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +public class MessageCode { + + public final static String USER_MANAGER_SERVICE_UNAVAILABLE = "general.service_unavailable.usermanager"; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ResourceNotFoundException.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ResourceNotFoundException.java new file mode 100644 index 0000000000000000000000000000000000000000..593c1b03132831ef8575d6042f42906fcc580d54 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ResourceNotFoundException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import de.itvsh.kop.common.errorhandling.FunctionalErrorCode; +import lombok.Getter; + +@Getter +public class ResourceNotFoundException extends FunctionalException { + + private static final long serialVersionUID = 1L; + private static final String MESSAGE_TEMPLATE = "Resource '%s' with id '%s' not found."; + + private final String resourceName; + private final transient Object id; + + public ResourceNotFoundException(Class<?> resource, Object id) { + super(buildErrorCode(resource, id)); + + this.resourceName = resource.getSimpleName(); + this.id = id; + } + + private static FunctionalErrorCode buildErrorCode(Class<?> resource, Object id) { + return () -> String.format(MESSAGE_TEMPLATE, resource.getSimpleName(), id.toString()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ServiceUnavailableException.java b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ServiceUnavailableException.java new file mode 100644 index 0000000000000000000000000000000000000000..5018f491388ef3ebe2f106bcf8e37d81d2ada651 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/errorhandling/ServiceUnavailableException.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import lombok.Getter; + +public class ServiceUnavailableException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + @Getter + private String messageCode; + + public ServiceUnavailableException(String messageCode, Throwable throwable) { + super("Service Unavailable", throwable); + + this.messageCode = messageCode; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFile.java b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFile.java new file mode 100644 index 0000000000000000000000000000000000000000..a9523ee7eb1e01f4b86829686196ec8f64190bb2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFile.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.binaryfile.FileId; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@Getter +public class OzgFile { + + @JsonIgnore + private FileId id; + private String name; + private long size; + + private String contentType; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d893991f0a0f32fc4ae08cff52f5ea5bb781a0b6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileMapper.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import org.mapstruct.Mapper; + +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; + +@Mapper +public interface OzgFileMapper { + + OzgFile toFile(GrpcOzgFile file); + + default FileId toFileId(String id) { + return FileId.from(id); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..7459a492f0b29b7c6ae832861165b5a730fd4a23 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileRemoteService.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.ozg.pluto.grpc.file.FileServiceGrpc.FileServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetAttachmentsRequest; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetRepresentationsRequest; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +public class OzgFileRemoteService { + + @GrpcClient("pluto") + private FileServiceBlockingStub fileServiceStub; + @Autowired + private ContextService contextService; + @Autowired + private OzgFileMapper fileMapper; + + public Stream<OzgFile> getAttachmentsByEingang(String eingangId) { + var response = fileServiceStub.getAttachments(buildGrpcGetAttachmentsRequest(eingangId)); + + return response.getFileList().stream().map(fileMapper::toFile); + } + + private GrpcGetAttachmentsRequest buildGrpcGetAttachmentsRequest(String eingangId) { + return GrpcGetAttachmentsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setEingangId(eingangId) + .build(); + } + + public Stream<OzgFile> getRepresentationsByEingang(String eingangId) { + var response = fileServiceStub.getRepresentations(buildGrpcGetRepresentationsRequest(eingangId)); + + return response.getFileList().stream().map(fileMapper::toFile); + } + + private GrpcGetRepresentationsRequest buildGrpcGetRepresentationsRequest(String eingangId) { + return GrpcGetRepresentationsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setEingangId(eingangId) + .build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileService.java new file mode 100644 index 0000000000000000000000000000000000000000..93d0e94db5d83905a27e3e50e841ab17bb75834a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/file/OzgFileService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class OzgFileService { + + @Autowired + private OzgFileRemoteService remoteService; + + public Stream<OzgFile> getAttachmentsByEingang(String eingangId) { + return remoteService.getAttachmentsByEingang(eingangId); + } + + public Stream<OzgFile> getRepresentationsByEingang(String eingangId) { + return remoteService.getRepresentationsByEingang(eingangId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserHelper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..44732514a548bb98b5d679dce430864485604ef3 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserHelper.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CurrentUserHelper { + static final String ROLE_PREFIX = "ROLE_"; + + public static final Predicate<String> HAS_ROLE = CurrentUserHelper::hasRole; + + private static final AuthenticationTrustResolver TRUST_RESOLVER = new AuthenticationTrustResolverImpl(); + private static final Predicate<Authentication> TRUSTED = auth -> !TRUST_RESOLVER.isAnonymous(auth); + + public static Authentication getAuthentication() { + return findAuthentication().orElseThrow(() -> new IllegalStateException("No authenticated User found")); + } + + public static Optional<Authentication> findAuthentication() { + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).filter(TRUSTED); + } + + public static boolean hasRole(String role) { + var auth = getAuthentication(); + + if ((Objects.isNull(auth)) || (Objects.isNull(auth.getPrincipal()))) { + return false; + } + Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); + return containsRole(authorities, role); + + } + + public static boolean containsRole(Collection<? extends GrantedAuthority> authorities, String role) { + String roleToCheck; + + if (Objects.nonNull(role) && !role.startsWith(ROLE_PREFIX)) { + roleToCheck = ROLE_PREFIX + role; + } else { + roleToCheck = role; + } + + if (Objects.isNull(authorities)) { + return false; + } + + return containsRole(roleToCheck, authorities); + } + + public static String prepareRoleForCheck(String role) { + if ((Objects.nonNull(role)) && (!role.startsWith(ROLE_PREFIX))) { + return ROLE_PREFIX + role; + } else { + return role; + } + } + + public static boolean containsRole(String role, Collection<? extends GrantedAuthority> authorities) { + return authorities.stream().anyMatch(a -> isAuthorityEquals(role, a.getAuthority())); + } + + private static boolean isAuthorityEquals(String role, String authority) { + String roleToCheck = prepareRoleForCheck(role); + return StringUtils.equalsIgnoreCase(role, authority) || StringUtils.equalsIgnoreCase(roleToCheck, authority); + } + + public static UserId getCurrentUserId() { + return UserId.from(getAuthentication().getName()); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserService.java new file mode 100644 index 0000000000000000000000000000000000000000..d1692e4371ca85fbb6778655cf4befe325a81c86 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/CurrentUserService.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.keycloak.KeycloakPrincipal; +import org.keycloak.representations.AccessToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.binaryfile.GoofyUserWithFileId; + +@Service +public class CurrentUserService { + + public static final String VERIFY_HAS_ROLE_VERWALTUNG_USER = "@currentUserService.hasRole(\"" + UserRole.VERWALTUNG_USER + "\")"; + + static final String USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID = "organisationseinheitId"; + + public boolean hasRole(String role) { + return CurrentUserHelper.hasRole(role); + } + + public Collection<GrantedAuthority> getAuthorities() { + return Collections.unmodifiableCollection(new HashSet<GrantedAuthority>(CurrentUserHelper.getAuthentication().getAuthorities())); + } + + public UserProfile getUser() { + var dlUser = getDownloadUser(); + if (dlUser.isPresent()) { + return dlUser.get(); + } + + Optional<AccessToken> token = getCurrentSecurityToken(); + + var userBuilder = UserProfile.builder() + .id(getUserId()) + .authorities(getAuthorities()); + + token.ifPresent(t -> userBuilder.userName(t.getPreferredUsername()) + .firstName(t.getGivenName()) + .lastName(t.getFamilyName()) + .organisationseinheitIds(getOrganisationseinheitId(t.getOtherClaims()))); + + return userBuilder.build(); + } + + public UserId getUserId() { + return CurrentUserHelper.getCurrentUserId(); + } + + List<String> getOrganisationseinheitId(Map<String, Object> claims) { + return Optional.ofNullable(claims.get(USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID)) + .map(col -> (Collection<?>) col).orElse(Collections.emptyList()) // NOSONAR - Collection.class::cast has type-safty issue + .stream().map(Object::toString).collect(Collectors.toList()); + } + + private Optional<UserProfile> getDownloadUser() { + return Optional.of(CurrentUserHelper.getAuthentication().getPrincipal()) + .filter(GoofyUserWithFileId.class::isInstance) + .map(GoofyUserWithFileId.class::cast) + .map(GoofyUserWithFileId::getUser); + + } + + private Optional<AccessToken> getCurrentSecurityToken() { + Object principal = CurrentUserHelper.getAuthentication().getPrincipal(); + + if (principal instanceof KeycloakPrincipal<?> kcPrincipal) { + return Optional.of(kcPrincipal.getKeycloakSecurityContext().getToken()); + } + + return Optional.empty(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserId.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserId.java new file mode 100644 index 0000000000000000000000000000000000000000..dcdd11eec3a4239cee505bb4d077c9e99adeb284 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserId.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.UUID; + +import de.itvsh.kop.common.datatype.StringBasedValue; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +@EqualsAndHashCode(callSuper = true) +public class UserId extends StringBasedValue { + + private static final long serialVersionUID = 1L; + + UserId(String userId) { + super(userId); + } + + public static UserId from(UUID userId) { + return UserId.from(userId.toString()); + } + + public static UserId from(String userId) { + return new UserId(userId); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserIdMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserIdMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..bd529d8262b0f3f011d20ee2aa775e4a12cf6ca6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserIdMapper.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.mapstruct.Mapper; + +@Mapper +public interface UserIdMapper { + + default UserId fromString(String userId) { + return Optional.ofNullable(StringUtils.trimToNull(userId)).map(UserId::from).orElse(null); + } + + default String toString(UserId userId) { + return userId == null ? null : userId.toString(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerProperties.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..54e219971217beb07fcec61a25dfdb74c559b9d8 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerProperties.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(UserManagerProperties.PREFIX) +public class UserManagerProperties { + + static final String PREFIX = "kop.user-manager"; + + private static final String MIGRATION_PATH = "/migration/user/{externalUserId}"; // NOSONAR + + private String url; + private String profileTemplate; + private String searchTemplate; + private String internalurl; + + String getFullInternalUrlTemplate() { + return internalurl + MIGRATION_PATH; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerUrlProvider.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerUrlProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..e6902afed4a132a89ab6b9cc25359002bf30c010 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserManagerUrlProvider.java @@ -0,0 +1,49 @@ +package de.itvsh.goofy.common.user; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.postfach.PostfachMail; + +@Service +public class UserManagerUrlProvider { + + public static final String SYSTEM_USER_IDENTIFIER = "system"; + public static final Predicate<PostfachMail> SENT_BY_CLIENT_USER = postfachNachricht -> Optional.ofNullable(postfachNachricht.getCreatedBy()) + .map(createdBy -> !createdBy.toString().startsWith(SYSTEM_USER_IDENTIFIER)).orElse(false); + + @Autowired + private UserManagerProperties userManagerProperties; + + public String getUserProfileTemplate() { + return userManagerProperties.getUrl() + userManagerProperties.getProfileTemplate(); + } + + public String getUserProfileSearchTemplate() { + return userManagerProperties.getUrl() + userManagerProperties.getSearchTemplate(); + } + + public String getInternalUserIdTemplate() { + return userManagerProperties.getFullInternalUrlTemplate(); + } + + public boolean isConfiguredForUserProfile() { + return Objects.nonNull(StringUtils.trimToNull(userManagerProperties.getUrl())) + && Objects.nonNull(StringUtils.trimToNull(userManagerProperties.getProfileTemplate())); + } + + public boolean isConfiguredForSearchUserProfile() { + return Objects.nonNull(StringUtils.trimToNull(userManagerProperties.getUrl())) + && Objects.nonNull(StringUtils.trimToNull(userManagerProperties.getSearchTemplate())); + } + + public boolean isConfiguredForInternalUserId() { + return Objects.nonNull(StringUtils.trimToNull(userManagerProperties.getInternalurl())); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfile.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..d53b318add4bda1b9af0ec95f4a880c1cbe68f95 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserProfile.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Collection; +import java.util.Date; + +import org.springframework.security.core.GrantedAuthority; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Singular; + +@Builder +@Getter +@AllArgsConstructor +public class UserProfile { + + @JsonIgnore + private UserId id; + private String userName; + private String firstName; + private String lastName; + private Date createdAt; + + @Singular + private Collection<GrantedAuthority> authorities; + @Singular + private Collection<String> organisationseinheitIds; + + public String getFullName() { + return String.format("%s %s", firstName, lastName); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..442632535cbd649159c0ec789b53b824e80906aa --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRemoteService.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.LinkedHashMap; +import java.util.Optional; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.keycloak.KeycloakPrincipal; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import de.itvsh.goofy.common.errorhandling.MessageCode; +import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Component +public class UserRemoteService { + + static final String FIRST_NAME_KEY = "firstName"; + static final String LAST_NAME_KEY = "lastName"; + + @Autowired + private UserManagerProperties userManagerProperties; + @Autowired + private UserManagerUrlProvider userManagerUrlProvider; + + private RestTemplate restTemplate = new RestTemplate(); + + public Optional<UserId> getUserId(UserId externalUserId) { + try { + if (userManagerUrlProvider.isConfiguredForInternalUserId()) { + var internalId = restTemplate.getForObject(userManagerProperties.getFullInternalUrlTemplate(), String.class, + externalUserId.toString()); + return StringUtils.isNotEmpty(internalId) ? Optional.of(UserId.from(internalId)) : Optional.empty(); + } else { + return Optional.empty(); + } + } catch (RestClientException e) { + LOG.warn("Error loading internal Userid.", e); + return Optional.empty(); + } + } + + public UserProfile getUser(UserId userId) { + return executeHandlingException(() -> getUserById(userId)); + } + + private <T> T executeHandlingException(Supplier<T> runnable) { + try { + return runnable.get(); + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + return null; + } + LOG.error("HttpClientErrorException: Error getting User by id.", e); + throw new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, e); + } catch (IllegalArgumentException e) { + LOG.error("IllegalArgumentException: Error getting User by id.", e); + throw new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, e); + } + } + + private UserProfile getUserById(UserId userId) { + return buildUser(getBodyMap(doExchange(userId))); + } + + private ResponseEntity<Object> doExchange(UserId userId) { + return restTemplate.exchange(buildUserProfileUri(userId), HttpMethod.GET, buildHttpEntityWithAuthorization(), Object.class); + } + + String buildUserProfileUri(UserId userId) { + return UriComponentsBuilder.fromUriString(String.format(userManagerUrlProvider.getInternalUserIdTemplate(), userId.toString())).toUriString(); + } + + private HttpEntity<Object> buildHttpEntityWithAuthorization() { + var headers = new HttpHeaders(); + headers.add("Authorization", "Bearer " + getToken()); + return new HttpEntity<>(headers); + } + + String getToken() { + var principle = (KeycloakPrincipal<?>) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + return principle.getKeycloakSecurityContext().getTokenString(); + } + + @SuppressWarnings("unchecked") + <T> LinkedHashMap<String, Object> getBodyMap(ResponseEntity<T> responseEntity) { + return (LinkedHashMap<String, Object>) responseEntity.getBody(); + } + + UserProfile buildUser(LinkedHashMap<String, Object> bodyMap) { + return UserProfile.builder() + .firstName((String) bodyMap.getOrDefault(FIRST_NAME_KEY, StringUtils.EMPTY)) + .lastName((String) bodyMap.getOrDefault(LAST_NAME_KEY, StringUtils.EMPTY)) + .build(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRole.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRole.java new file mode 100644 index 0000000000000000000000000000000000000000..7f60273540b4aae49bc7d35ec2d0245da17fb976 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserRole.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class UserRole { + + public static final String EINHEITLICHER_ANSPRECHPARTNER = "EINHEITLICHER_ANSPRECHPARTNER"; + public static final String VERWALTUNG_POSTSTELLE = "VERWALTUNG_POSTSTELLE"; + public static final String VERWALTUNG_USER = "VERWALTUNG_USER"; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java new file mode 100644 index 0000000000000000000000000000000000000000..4c970317e1433ca620e90cb9c83971987b1bdde6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/common/user/UserService.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + + @Autowired + private UserRemoteService remoteService; + + public UserProfile getById(UserId userId) { + return remoteService.getUser(userId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieCommandHandler.java b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieCommandHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..76e1d9af249bc2a45cd7dda5cec3c294ff307031 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieCommandHandler.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandBodyMapper; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.kommentar.Kommentar; +import de.itvsh.goofy.wiedervorlage.Wiedervorlage; + +@Component +class HistorieCommandHandler { + + static final String DIRECTION_INCOMMING = "IN"; + static final String DIRECTION_OUTGOING = "OUT"; + static final String DIRECTION = "direction"; + static final String MAIL_SERVICE = "MailService"; + static final String CLIENT = "client"; + + boolean isHistorieCommand(Command command) { + return !isOutgoingPostfachNachrichtByMailService(command); + } + + boolean isOutgoingPostfachNachrichtByMailService(Command command) { + return command.getOrder().equals(CommandOrder.CREATE_ATTACHED_ITEM) && isMailService(command) && isOutgoing(command); + } + + private boolean isMailService(Command command) { + var client = command.getBody().get(CLIENT); + return Objects.nonNull(client) && ((String) client).equals(MAIL_SERVICE); + } + + private boolean isOutgoing(Command command) { + return isDirection(command, DIRECTION_OUTGOING); + } + + private boolean isIncomming(Command command) { + return isDirection(command, DIRECTION_INCOMMING); + } + + private boolean isDirection(Command command, String expectedDirection) { + var direction = getItemMap(command).get(DIRECTION); + return Objects.nonNull(direction) && ((String) direction).equals(expectedDirection); + } + + Command translateOrder(Command command) { + HistorieCommandHandler translator = new HistorieCommandHandler(); + return switch (command.getOrder()) { + case CREATE_ATTACHED_ITEM: + yield translator.mapCreateOrder(command); + case UPDATE_ATTACHED_ITEM: + yield translator.mapUpdateOrder(command); + case PATCH_ATTACHED_ITEM: + yield translator.mapPatchOrder(command); + case SEND_POSTFACH_MAIL: + yield command.toBuilder().order(CommandOrder.SEND_POSTFACH_NACHRICHT).build(); + default: + yield command; + }; + } + + private Command mapCreateOrder(Command command) { + var resultBuilder = command.toBuilder(); + var itemName = getItemName(command); + + itemName.ifPresent(name -> { + if (name.equals(Kommentar.class.getSimpleName())) { + resultBuilder.order(CommandOrder.CREATE_KOMMENTAR).build(); + } else if (name.equals(Wiedervorlage.class.getSimpleName())) { + resultBuilder.order(CommandOrder.CREATE_WIEDERVORLAGE).build(); + } else if (isMailService(command) && isIncomming(command)) { + resultBuilder.order(CommandOrder.RECEIVE_POSTFACH_NACHRICHT).build(); + } + }); + + return resultBuilder.build(); + } + + private Command mapUpdateOrder(Command command) { + var resultBuilder = command.toBuilder(); + var itemName = getItemName(command); + + itemName.ifPresent(name -> { + if (name.equals(Kommentar.class.getSimpleName())) { + resultBuilder.order(CommandOrder.EDIT_KOMMENTAR).build(); + } else if (name.equals(Wiedervorlage.class.getSimpleName())) { + resultBuilder.order(CommandOrder.EDIT_WIEDERVORLAGE).build(); + } + }); + + return resultBuilder.build(); + } + + private Command mapPatchOrder(Command command) { + var resultBuilder = command.toBuilder(); + var isDone = getDoneValue(command); + + isDone.ifPresent(done -> { + if (done.booleanValue()) { + resultBuilder.order(CommandOrder.WIEDERVORLAGE_ERLEDIGEN).build(); + } else { + resultBuilder.order(CommandOrder.WIEDERVORLAGE_WIEDEREROEFFNEN).build(); + } + }); + + return resultBuilder.build(); + } + + private Optional<String> getItemName(Command command) { + return Optional.ofNullable((String) command.getBody().get(CommandBodyMapper.ITEM_NAME_PROPERTY)); + } + + private Optional<Boolean> getDoneValue(Command command) { + var body = getItemMap(command); + return body.containsKey("done") ? Optional.of(Boolean.valueOf(String.valueOf(body.get("done")))) : Optional.empty(); + } + + @SuppressWarnings("unchecked") + private Map<String, Object> getItemMap(Command command) { + return (Map<String, Object>) command.getBody().get(CommandBodyMapper.ITEM_PROPERTY); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieController.java b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieController.java new file mode 100644 index 0000000000000000000000000000000000000000..af4f7cca062e09bb3eacb3b4cac42c452f9822ff --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieController.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.command.Command; + +@RestController +@RequestMapping(HistorieController.PATH) +public class HistorieController { + static final String PATH = "/api/histories"; // NOSONAR + static final String PARAM_VORGANG_ID = "vorgangId"; + + @Autowired + private HistorieService historieService; + + @Autowired + private HistorieModelAssembler modelAssembler; + + @GetMapping(params = PARAM_VORGANG_ID) + public CollectionModel<EntityModel<Command>> getHistorieList(@RequestParam String vorgangId) { + return modelAssembler + .toCollectionModel(historieService.findFinishedCommands(vorgangId)); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..c224c02c4c5f2cf8303cf3ba8d8ae2c91651d9d3 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieModelAssembler.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; + +@Component +class HistorieModelAssembler implements RepresentationModelAssembler<Command, EntityModel<Command>> { + static final String SYSTEM_NOTIFICATION_MANAGER_PREFIX = "system-notification_manager"; + + static final String REL_ASSIGNED_TO = "assignedTo"; + + static final String ASSIGNED_TO_BODY_FIELD = "assignedTo"; + + @Autowired + private UserManagerUrlProvider userManagerUrlProvider; + + @Override + public EntityModel<Command> toModel(Command entity) { + var cleanCommand = unsetSystemUser(entity); + var modelBuilder = ModelBuilder.fromEntity(cleanCommand).addLink(linkTo(HistorieController.class).slash(cleanCommand.getId()).withSelfRel()); + + addAssignedTo(cleanCommand, modelBuilder); + + return modelBuilder.buildModel(); + } + + Command unsetSystemUser(Command entity) { + if (isSystemNotificationUser(entity)) { + return entity.toBuilder().createdBy(null).build(); + } + + return entity; + } + + private boolean isSystemNotificationUser(Command entity) { + return Objects.nonNull(entity.getCreatedBy()) && entity.getCreatedBy().toString().startsWith(SYSTEM_NOTIFICATION_MANAGER_PREFIX); + } + + private void addAssignedTo(Command entity, ModelBuilder<Command> modelBuilder) { + Optional.ofNullable(entity.getBody()).map(body -> body.get(ASSIGNED_TO_BODY_FIELD)) + .ifPresent(assignedTo -> { + if (userManagerUrlProvider.isConfiguredForUserProfile()) { + modelBuilder.addLink(Link.of(String.format(userManagerUrlProvider.getUserProfileTemplate(), assignedTo), REL_ASSIGNED_TO)); + } + }); + } + + public CollectionModel<EntityModel<Command>> toCollectionModel(Stream<Command> entities) { + return CollectionModel.of(entities.map(this::toModel).toList(), linkTo(HistorieController.class).withSelfRel()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieService.java b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieService.java new file mode 100644 index 0000000000000000000000000000000000000000..f3a8aef9aa3d8f4376481210f771b249a2fc4263 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/historie/HistorieService.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandService; + +@Service +class HistorieService { + + @Autowired + private CommandService commandService; + + @Autowired + private HistorieCommandHandler historieCommandHandler; + + public Stream<Command> findFinishedCommands(String vorgangId) { + return commandService.findFinishedCommands(vorgangId) + .filter(historieCommandHandler::isHistorieCommand) + .map(historieCommandHandler::translateOrder); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/Kommentar.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/Kommentar.java new file mode 100644 index 0000000000000000000000000000000000000000..e92ce16ffa5d9c053971a76412a6dfdd26faf61f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/Kommentar.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static de.itvsh.goofy.common.ValidationMessageCodes.*; + +import java.time.ZonedDateTime; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.command.CommandBody; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@Builder(toBuilder = true) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@ToString +public class Kommentar implements CommandBody { + + @JsonIgnore + private String id; + @JsonIgnore + private long version; + + @JsonIgnore + private String vorgangId; + + @JsonProperty(access = Access.READ_ONLY) + @LinkedUserProfileResource + private String createdBy; + @JsonProperty(access = Access.READ_ONLY) + private ZonedDateTime createdAt; + + @NotNull(message = FIELD_IS_EMPTY) + @Size(min = 1, message = FIELD_MIN_SIZE) + private String text; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommand.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..273268bf150709de58c26740c95da90f1da3dee7 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import javax.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandStatus; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@EqualsAndHashCode +class KommentarCommand { + + @JsonIgnore + private String id; + + private CommandOrder order; + + @JsonIgnore + private CommandStatus status; + + @Valid + private Kommentar kommentar; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommandController.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommandController.java new file mode 100644 index 0000000000000000000000000000000000000000..f8158517b850676d67c9f971fb139a56cb141e54 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarCommandController.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandController; + +@RestController +@RequestMapping(KommentarCommandController.KOMMENTAR_COMMANDS) +public class KommentarCommandController { + + static final String KOMMENTAR_COMMANDS = "/api/kommentars/{kommentarId}/{kommentarVersion}/commands"; + + @Autowired + private KommentarService service; + + @PostMapping + public ResponseEntity<Void> editKommentar(@RequestBody KommentarCommand kommentarCommand, @PathVariable String kommentarId, + @PathVariable long kommentarVersion) { + var createdCommand = service.editKommentar(kommentarCommand.getKommentar(), kommentarId, kommentarVersion); + + return buildResponseLink(createdCommand); + } + + private ResponseEntity<Void> buildResponseLink(Command createdKommentarCommand) { + return ResponseEntity.created(linkTo(CommandController.class).slash(createdKommentarCommand.getId()).toUri()).build(); + } + + @RestController + @RequestMapping(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG) + public static class KommentarCommandByVorgangController { + + static final String KOMMENTAR_COMMANDS_BY_VORGANG = "/api/vorgangs/{vorgangId}/kommentarCommands"; + + @Autowired + private KommentarService service; + + @PostMapping + public ResponseEntity<Void> createKommentar(@RequestBody KommentarCommand command, @PathVariable String vorgangId) { + var createdCommand = service.createKommentar(command.getKommentar(), vorgangId); + + return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarController.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarController.java new file mode 100644 index 0000000000000000000000000000000000000000..80bbf05f26043b46e39cf5871599111fb179c95e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarController.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.util.Comparator; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(KommentarController.KOMMENTAR_PATH) +public class KommentarController { + + static final String KOMMENTAR_PATH = "/api/kommentars"; // NOSONAR + + @Autowired + private KommentarService service; + + @Autowired + private KommentarModelAssembler modelAssembler; + + @GetMapping("/{kommentarId}") + public EntityModel<Kommentar> getById(@PathVariable String kommentarId) { + return modelAssembler.toModel(service.getById(kommentarId)); + } + + @RestController + @RequestMapping(KommentarByVorgangController.KOMMENTAR_BY_VORGANG_PATH) + public static class KommentarByVorgangController { + + static final String KOMMENTAR_BY_VORGANG_PATH = "/api/vorgangs"; // NOSONAR + + @Autowired + private KommentarService service; + + @Autowired + private KommentarModelAssembler modelAssembler; + + @GetMapping("/{vorgangId}/kommentars") + public CollectionModel<EntityModel<Kommentar>> getAll(@PathVariable String vorgangId) { + return modelAssembler.toCollectionModel(sortByCreatedAt(service.findByVorgangId(vorgangId)), vorgangId); + } + + Stream<Kommentar> sortByCreatedAt(Stream<Kommentar> kommentare) { + return kommentare.sorted(Comparator.comparing(Kommentar::getCreatedAt).reversed()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..2c4b648c6e4fc37656afa54861413a5c897016eb --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarMapper.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.time.ZonedDateTime; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.ValueMapping; +import org.springframework.beans.factory.annotation.Autowired; + +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItem; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +abstract class KommentarMapper { + + static final String ID = "id"; + static final String TEXT = "text"; + static final String CREATED_BY = "createdBy"; + static final String CREATED_AT = "createdAt"; + + @Autowired + private GrpcObjectMapper grpcObjectMapper; + + @Mapping(target = "kommentar", ignore = true) + @ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL) + @ValueMapping(source = "UNDEFINED", target = MappingConstants.NULL) + abstract KommentarCommand toKommentarCommand(GrpcCommand command); + + public Kommentar fromItem(GrpcVorgangAttachedItem vorgangAttachedItem) { + return fromItemMap(grpcObjectMapper.mapFromGrpc(vorgangAttachedItem.getItem()), vorgangAttachedItem.getVersion()); + } + + Kommentar fromItemMap(Map<String, Object> map, long version) { + return Kommentar.builder() + .id((String) map.get(ID)) + .version(version) + .createdAt(ZonedDateTime.parse((String) map.get(CREATED_AT))) + .createdBy((String) map.get(CREATED_BY)) + .text((String) map.get(TEXT)) + .build(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..2f8a985debd3fabc4abb8a39acbba11f9f6b114d --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarModelAssembler.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.kommentar.KommentarCommandController.KommentarCommandByVorgangController; + +@Component +class KommentarModelAssembler implements RepresentationModelAssembler<Kommentar, EntityModel<Kommentar>> { + + static final String REL_CREATE = "create-kommentar"; + static final String REL_EDIT = "edit"; + + @Override + public EntityModel<Kommentar> toModel(Kommentar kommentar) { + var selfLink = linkTo(KommentarController.class).slash(kommentar.getId()); + var commandLink = linkTo(methodOn(KommentarCommandController.class).editKommentar(null, kommentar.getId(), kommentar.getVersion())); + + return ModelBuilder.fromEntity(kommentar).addLink(selfLink.withSelfRel()) + .addLink(commandLink.withRel(REL_EDIT)) + .buildModel(); + } + + public CollectionModel<EntityModel<Kommentar>> toCollectionModel(Stream<Kommentar> entities, String vorgangId) { + return CollectionModel.of(entities.map(this::toModel).toList(), + linkTo(KommentarController.class).withSelfRel(), + linkTo(methodOn(KommentarCommandByVorgangController.class).createKommentar(null, vorgangId)) + .withRel(REL_CREATE)); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..d7823ca6a5cbaa5131031bd1289759b41c9db0ce --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarRemoteService.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.GoofyServerApplication; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class KommentarRemoteService { + + static final String ITEM_NAME = "Kommentar"; + + @GrpcClient(GoofyServerApplication.GRPC_CLIENT) + private VorgangAttachedItemServiceBlockingStub vorgangAttachedItemServiceStub; + @Autowired + private KommentarMapper mapper; + + public Stream<Kommentar> findByVorgangId(String vorgangId) { + var response = vorgangAttachedItemServiceStub.find(buildFindRequest(vorgangId)); + + return response.getVorgangAttachedItemsList().stream().map(mapper::fromItem); + } + + GrpcFindVorgangAttachedItemRequest buildFindRequest(String vorgangId) { + return GrpcFindVorgangAttachedItemRequest.newBuilder() + .setVorgangId(vorgangId) + .setItemName(ITEM_NAME) + .build(); + } + + public Kommentar getById(String kommentarId) { + var response = vorgangAttachedItemServiceStub.getById(buildRequest(kommentarId)); + + return mapper.fromItem(response.getVorgangAttachedItem()).toBuilder().vorgangId(response.getVorgangAttachedItem().getVorgangId()).build(); + } + + private GrpcVorgangAttachedItemRequest buildRequest(String kommentarId) { + return GrpcVorgangAttachedItemRequest.newBuilder().setId(kommentarId).build(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarService.java b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarService.java new file mode 100644 index 0000000000000000000000000000000000000000..175908fecd85ef601c0e8a5ef7e4f077273fa6a0 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/kommentar/KommentarService.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.time.ZonedDateTime; +import java.util.stream.Stream; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import de.itvsh.goofy.common.attacheditem.VorgangAttachedItemService; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.user.CurrentUserService; + +@Validated +@Service +class KommentarService { + + @Autowired + private KommentarRemoteService remoteService; + @Autowired + private CurrentUserService currentUserService; + @Autowired + private VorgangAttachedItemService vorgangAttachedItemService; + + public Command createKommentar(@Valid Kommentar kommentar, String vorgangId) { + return vorgangAttachedItemService.createNewKommentar(addCreated(kommentar), vorgangId); + } + + Kommentar addCreated(Kommentar kommentar) { + return kommentar.toBuilder() + .createdAt(ZonedDateTime.now().withNano(0)) + .createdBy(currentUserService.getUserId().toString()) + .build(); + } + + public Command editKommentar(@Valid Kommentar kommentar, String kommentarId, long kommentarVersion) { + var loadedKommentar = getById(kommentarId); + var preparedKommentar = loadedKommentar.toBuilder().text(kommentar.getText()).build(); + + return vorgangAttachedItemService.editKommentar(preparedKommentar, kommentarId, kommentarVersion); + } + + public Stream<Kommentar> findByVorgangId(String vorgangId) { + return remoteService.findByVorgangId(vorgangId); + } + + public Kommentar getById(String kommentarId) { + return remoteService.getById(kommentarId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMail.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMail.java new file mode 100644 index 0000000000000000000000000000000000000000..fe0443d976fb79e6d667d27c99e1d95a436cf6b9 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMail.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static de.itvsh.goofy.common.ValidationMessageCodes.*; + +import java.time.ZonedDateTime; +import java.util.List; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.LinkedResource; +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.command.CommandBody; +import de.itvsh.goofy.common.user.UserId; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.ToString; + +@Builder(toBuilder = true) +@Getter +@ToString +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PUBLIC) +public class PostfachMail implements CommandBody { + + enum Direction { + IN, OUT + } + + @JsonIgnore + private String id; + @JsonIgnore + private String postfachId; + @JsonIgnore + private String vorgangId; + + private ZonedDateTime createdAt; + @JsonIgnore + private UserId createdBy; + + private ZonedDateTime sentAt; + private Boolean sentSuccessful; + private String messageCode; + + private Direction direction; + private ReplyOption replyOption; + + @NotEmpty(message = FIELD_IS_EMPTY) + @Size(max = 70, message = FIELD_MAX_SIZE) + private String subject; + + @NotEmpty(message = FIELD_IS_EMPTY) + private String mailBody; + + @LinkedResource(controllerClass = BinaryFileController.class) + @Singular + private List<FileId> attachments; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java new file mode 100644 index 0000000000000000000000000000000000000000..4ffd89afaa0590440f0a7439f7a33ed513612192 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailController.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.vorgang.Antragsteller; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangWithEingang; + +@RestController +@RequestMapping(PostfachMailController.PATH) +public class PostfachMailController { + + public static final String PATH = "/api/postfachMails"; // NOSONAR + + public static final String PARAM_VORGANG_ID = "vorgangId"; + + static final String PDF_NAME_TEMPLATE = "%s_%s_Nachrichten.pdf"; + static final SimpleDateFormat PDF_NAME_DATE_FORMATTER = new SimpleDateFormat("YYYYMMDD"); + + @Autowired + private PostfachMailService service; + @Autowired + private PostfachMailModelAssembler assembler; + + @Autowired + private VorgangController vorgangController; + @Autowired + private BinaryFileController binaryFileController; + + @GetMapping(params = PARAM_VORGANG_ID) + public CollectionModel<EntityModel<PostfachMail>> getAll(@RequestParam String vorgangId) { + var vorgang = getVorgang(vorgangId); + + return assembler.toCollectionModel(sort(service.getAll(vorgangId)), vorgang, getPostfachId(vorgang)); + } + + @GetMapping(params = PARAM_VORGANG_ID, produces = MediaType.APPLICATION_PDF_VALUE) + public ResponseEntity<StreamingResponseBody> getAllAsPdf(@RequestParam String vorgangId) { + var vorgang = getVorgang(vorgangId); + + return buildResponseEntity(vorgang, createDownloadStreamingBody(vorgang)); + } + + StreamingResponseBody createDownloadStreamingBody(VorgangWithEingang vorgang) { + return out -> service.getAllAsPdf(vorgang, out); + } + + ResponseEntity<StreamingResponseBody> buildResponseEntity(VorgangWithEingang vorgang, StreamingResponseBody responseBody) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, buildPdfName(vorgang)) + .contentType(MediaType.APPLICATION_PDF) + .body(responseBody); + } + + private String buildPdfName(VorgangWithEingang vorgang) { + return String.format(PDF_NAME_TEMPLATE, vorgang.getNummer(), PDF_NAME_DATE_FORMATTER.format(new Date())); + } + + private VorgangWithEingang getVorgang(String vorgangId) { + return vorgangController.getVorgang(vorgangId); + } + + Optional<String> getPostfachId(VorgangWithEingang vorgang) { + return Optional.ofNullable(vorgang.getEingang().getAntragsteller()).map(Antragsteller::getPostfachId).map(StringUtils::trimToNull); + } + + private Stream<PostfachMail> sort(Stream<PostfachMail> nachrichten) { + return nachrichten.sorted(new PostfachNachrichtComparator().reversed()); + } + + public boolean isPostfachConfigured() { + return service.isPostfachConfigured(); + } + + @GetMapping("{nachrichtId}/attachments") + public CollectionModel<EntityModel<OzgFile>> findAttachments(@PathVariable PostfachNachrichtId nachrichtId) { + var postfachNachricht = service.findById(nachrichtId); + + return binaryFileController.getFiles(postfachNachricht.getAttachments()); + } + + @RestController + @RequestMapping(PostfachMailCommandController.POSTFACH_MAIL_COMMAND_PATH) + public static class PostfachMailCommandController { + + static final String POSTFACH_MAIL_COMMAND_PATH = "/api/vorgangs/{vorgangId}/postfachMails/{postfachMailId}/commands"; // NOSONAR + + @Autowired + private PostfachMailService service; + @Autowired + private CommandByRelationController commandByRelationController; + + @PostMapping + public ResponseEntity<Void> createCommand(@PathVariable String vorgangId, @PathVariable String postfachMailId, + @RequestBody CreateCommand command) { + command = command.toBuilder() + .vorgangId(vorgangId) + .relationId(postfachMailId) + .build(); + var created = commandByRelationController.createCommand(command); + + service.resendPostfachMail(created.getId(), postfachMailId); + + return ResponseEntity.created(linkTo(CommandController.class).slash(created.getId()).toUri()).build(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..910fc2ae3ce6ed68f03e97fa834d1ddaf7aa7f1c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailMapper.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ValueMapping; + +import com.google.protobuf.ProtocolStringList; + +import de.itvsh.goofy.common.TimeMapper; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.ozg.mail.postfach.GrpcPostfachMail; + +@Mapper(uses = { TimeMapper.class, UserIdMapper.class }, // + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +interface PostfachMailMapper { + + @Mapping(target = "attachment", ignore = true) + @ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL) + @ValueMapping(source = "UNDEFINED", target = MappingConstants.NULL) + @Mapping(target = "attachments", source = "attachmentList") + PostfachMail toPostfachMail(GrpcPostfachMail grpcPostfachMail); + + default List<FileId> fromStringList(ProtocolStringList stringList) { + return stringList.stream().map(FileId::from).toList(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..6914d78d08db17897bea3bdb3b92ae7567718d42 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailModelAssembler.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.postfach.PostfachMailController.PostfachMailCommandController; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangWithEingang; + +@Component +class PostfachMailModelAssembler implements RepresentationModelAssembler<PostfachMail, EntityModel<PostfachMail>> { + + public static final String REL_SEND_POSTFACH_MAIL = "sendPostfachMail"; + public static final String REL_RESEND_POSTFACH_MAIL = "resendPostfachMail"; + public static final String REL_RESET_NEW_POSTFACH_MAIL = "resetHasNewPostfachNachricht"; + public static final String REL_ATTACHMENTS = "attachments"; + static final String REL_UPLOAD_ATTACHMENT = "uploadAttachment"; + static final String REL_CREATED_BY = "createdBy"; + + private static final Predicate<PostfachMail> SENT_FAILED = postfachMail -> BooleanUtils.isFalse(postfachMail.getSentSuccessful()); + private static final Predicate<PostfachMail> HAS_ATTACHMENTS = nachricht -> CollectionUtils.isNotEmpty(nachricht.getAttachments()); + + static final String SYSTEM_USER_IDENTIFIER = "system"; + private static final Predicate<PostfachMail> SENT_BY_CLIENT_USER = postfachNachricht -> Optional.ofNullable(postfachNachricht.getCreatedBy()) + .map(createdBy -> !createdBy.toString().startsWith(SYSTEM_USER_IDENTIFIER)).orElse(false); + + @Autowired + private UserManagerUrlProvider userManagerUrlProvider; + + public CollectionModel<EntityModel<PostfachMail>> toCollectionModel(Stream<PostfachMail> entities, VorgangWithEingang vorgang, + Optional<String> postfachId) { + CollectionModel<EntityModel<PostfachMail>> model = CollectionModel.of(entities.map(this::toModel).toList(), + linkTo(PostfachMailController.class).withSelfRel()); + + postfachId.ifPresent(id -> addPostfachNachrichtLinks(model, vorgang)); + + return model; + } + + @Override + public EntityModel<PostfachMail> toModel(PostfachMail postfachMail) { + var selfLink = linkTo(PostfachMailController.class).slash(postfachMail.getId()); + + return ModelBuilder.fromEntity(postfachMail).addLink(selfLink.withSelfRel()) + .ifMatch(SENT_FAILED) + .addLink(linkTo(methodOn(PostfachMailCommandController.class).createCommand(postfachMail.getVorgangId(), postfachMail.getId(), null)) + .withRel(REL_RESEND_POSTFACH_MAIL)) + .ifMatch(HAS_ATTACHMENTS) + .addLink(linkTo(methodOn(PostfachMailController.class).findAttachments(PostfachNachrichtId.from(postfachMail.getId()))) + .withRel(REL_ATTACHMENTS)) + .ifMatch(() -> userManagerUrlProvider.isConfiguredForUserProfile() && SENT_BY_CLIENT_USER.test(postfachMail)) + .addLink(() -> Link.of(String.format(userManagerUrlProvider.getUserProfileTemplate(), postfachMail.getCreatedBy()), REL_CREATED_BY)) + .buildModel(); + } + + private void addPostfachNachrichtLinks(CollectionModel<EntityModel<PostfachMail>> model, VorgangWithEingang vorgang) { + var vorgangId = vorgang.getId(); + model.add(linkTo(methodOn(CommandByRelationController.class).createCommand(vorgangId, vorgangId, vorgang.getVersion(), null)) + .withRel(REL_SEND_POSTFACH_MAIL)); + model.add(linkTo(BinaryFileController.class).slash(vorgangId).slash("postfachNachrichtAttachment").slash("file") + .withRel(REL_UPLOAD_ATTACHMENT)); + + if (vorgang.isHasNewPostfachNachricht()) { + model.add(linkTo(VorgangController.class).slash(vorgangId).slash("hasNewPostfachNachricht").withRel(REL_RESET_NEW_POSTFACH_MAIL)); + } + } + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..041696c0498784907938a8421045a513d1346ff5 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailRemoteService.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailRequest; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailsRequest; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailsResponse; +import de.itvsh.ozg.mail.postfach.GrpcIsPostfachConfiguredRequest; +import de.itvsh.ozg.mail.postfach.GrpcResendPostfachMailRequest; +import de.itvsh.ozg.mail.postfach.PostfachServiceGrpc.PostfachServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class PostfachMailRemoteService { + + @GrpcClient("pluto") + private PostfachServiceBlockingStub serviceStub; + @Autowired + private ContextService contextService; + @Autowired + private PostfachMailMapper postfachNachrichtMapper; + + public Stream<PostfachMail> findPostfachMails(String vorgangId) { + GrpcFindPostfachMailsResponse response = serviceStub.findPostfachMails(buildFindPostfachMailsRequest(vorgangId)); + + return response.getMailsList().stream().map(postfachNachrichtMapper::toPostfachMail); + } + + private GrpcFindPostfachMailsRequest buildFindPostfachMailsRequest(String vorgangId) { + return GrpcFindPostfachMailsRequest.newBuilder() + .setVorgangId(vorgangId) + .setContext(contextService.createCallContext()) + .build(); + } + + public Optional<PostfachMail> findById(PostfachNachrichtId nachrichtId) { + var response = serviceStub.findPostfachMail(buildFindPostfachMailRequest(nachrichtId)); + + return Optional.ofNullable(response.getNachricht()).map(postfachNachrichtMapper::toPostfachMail); + } + + private GrpcFindPostfachMailRequest buildFindPostfachMailRequest(PostfachNachrichtId nachrichtId) { + return GrpcFindPostfachMailRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setNachrichtId(nachrichtId.toString()) + .build(); + } + + public void resendPostfachMail(String commandId, String postfachMailId) { + serviceStub.resendPostfachMail(buildResendPostfachMailRequest(commandId, postfachMailId)); + } + + GrpcResendPostfachMailRequest buildResendPostfachMailRequest(String commandId, String postfachMailId) { + return GrpcResendPostfachMailRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setPostfachMailId(postfachMailId) + .setCommandId(commandId) + .build(); + } + + public boolean isPostfachConfigured() { + return serviceStub.isPostfachConfigured(buildIsPostfachConfiguredRequest()).getIsConfigured(); + } + + private GrpcIsPostfachConfiguredRequest buildIsPostfachConfiguredRequest() { + return GrpcIsPostfachConfiguredRequest.newBuilder() + .setContext(contextService.createCallContext()) + .build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailService.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailService.java new file mode 100644 index 0000000000000000000000000000000000000000..16fea2fbb687b990248eada729fb11adecc7ffd0 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachMailService.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.binaryfile.BinaryFileService; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.errorhandling.ResourceNotFoundException; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.common.user.UserService; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Service +class PostfachMailService { + + private Boolean isPostfachConfigured = null; + + @Autowired + private PostfachNachrichtPdfService pdfService; + + @Autowired + private PostfachMailRemoteService remoteService; + + @Autowired + private BinaryFileService fileService; + + @Autowired + private UserService userService; + + public PostfachMail findById(PostfachNachrichtId nachrichtId) { + return remoteService.findById(nachrichtId) + .orElseThrow(() -> new ResourceNotFoundException(PostfachMail.class, nachrichtId)); + } + + // @Async FIXME Er scheint den CallContext zu verlieren + public void resendPostfachMail(String commandId, String postfachMailId) { + try { + remoteService.resendPostfachMail(commandId, postfachMailId); + } catch (RuntimeException e) { + LOG.error("Error sending PostfachNachricht: " + e.getMessage(), e); +// FIXME mark command als error + } + } + + public boolean isPostfachConfigured() { + if (Objects.isNull(isPostfachConfigured)) { + LOG.info("isPostfachConfigured is null"); + isPostfachConfigured = remoteService.isPostfachConfigured(); + LOG.info("isPostfachConfigured set to " + isPostfachConfigured); + } + return isPostfachConfigured; + } + + public OutputStream getAllAsPdf(VorgangWithEingang vorgang, OutputStream out) { + var postfachNachrichtPdfDataList = buildPostfachNachrichtPdfDataList(vorgang.getId()); + + return pdfService.getAllAsPdf(vorgang, postfachNachrichtPdfDataList, out); + } + + Stream<PostfachNachrichtPdfData> buildPostfachNachrichtPdfDataList(String vorgangId) { + var postfachNachrichten = getAll(vorgangId).toList(); + var ozgFileIdOzgFileMap = getFiles(postfachNachrichten.stream()).collect(Collectors.toMap(OzgFile::getId, OzgFile::getName)); + + return postfachNachrichten.stream().map(postfachNachricht -> buildPostfachNachrichtPdfData(postfachNachricht, ozgFileIdOzgFileMap)); + } + + public Stream<PostfachMail> getAll(String vorgangId) { + return remoteService.findPostfachMails(vorgangId); + } + + private Stream<OzgFile> getFiles(Stream<PostfachMail> postfachMails) { + return fileService.getFiles(getFileIdsFromAllAttachments(postfachMails)); + } + + List<FileId> getFileIdsFromAllAttachments(Stream<PostfachMail> postfachMails) { + return postfachMails.map(PostfachMail::getAttachments) + .flatMap(Collection::stream) + .toList(); + } + + PostfachNachrichtPdfData buildPostfachNachrichtPdfData(PostfachMail postfachNachricht, + Map<FileId, String> ozgFileIdOzgFileMap) { + return PostfachNachrichtPdfData.builder() + .createdAt(postfachNachricht.getCreatedAt()) + .user(getUser(postfachNachricht)) + .direction(postfachNachricht.getDirection()) + .mailBody(postfachNachricht.getMailBody()) + .subject(postfachNachricht.getSubject()) + .attachmentNames(postfachNachricht.getAttachments().stream().map(ozgFileIdOzgFileMap::get).toList()) + .build(); + } + + UserProfile getUser(PostfachMail postfachNachricht) { + var createdBy = postfachNachricht.getCreatedBy(); + if (UserManagerUrlProvider.SENT_BY_CLIENT_USER.test(postfachNachricht)) { + return Optional.ofNullable(userService.getById(createdBy)).orElseGet(() -> null); + } + return Optional.ofNullable(createdBy) + .map(created -> UserProfile.builder().id(created).build()) + .orElseGet(() -> null); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtComparator.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtComparator.java new file mode 100644 index 0000000000000000000000000000000000000000..a8155bf3ecce866bd01a92fdbaef858f3849c7b4 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtComparator.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.util.Comparator; +import java.util.Objects; + +class PostfachNachrichtComparator implements Comparator<PostfachMail> { + + @Override + public int compare(PostfachMail o1, PostfachMail o2) { + var comp1 = Objects.isNull(o1.getSentAt()) ? o1.getCreatedAt() : o1.getSentAt(); + var comp2 = Objects.isNull(o2.getSentAt()) ? o2.getCreatedAt() : o2.getSentAt(); + return comp1.compareTo(comp2); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtId.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtId.java new file mode 100644 index 0000000000000000000000000000000000000000..1ea92c5191e68d82770a596e12fb34c4c8e95972 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtId.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import de.itvsh.kop.common.datatype.StringBasedValue; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +public class PostfachNachrichtId extends StringBasedValue { + + private static final long serialVersionUID = 1L; + + public PostfachNachrichtId(String nachrichtId) { + super(nachrichtId); + } + + public static PostfachNachrichtId from(String nachrichtId) { + return new PostfachNachrichtId(nachrichtId); + } + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfData.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfData.java new file mode 100644 index 0000000000000000000000000000000000000000..fe208292b21a27a2f3c94c9e41ac8d9f3e6e883a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfData.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.time.ZonedDateTime; +import java.util.List; + +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.postfach.PostfachMail.Direction; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +class PostfachNachrichtPdfData { + + private ZonedDateTime createdAt; + private UserProfile user; + + private String subject; + private String mailBody; + private Direction direction; + + private List<String> attachmentNames; + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfModel.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfModel.java new file mode 100644 index 0000000000000000000000000000000000000000..402876136b533c8665d75abfd84740ae5a3e467b --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfModel.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@XmlRootElement +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PACKAGE) +class PostfachNachrichtPdfModel { + + @XmlElement + private String vorgangName; + + @XmlElement + private String vorgangNummer; + + @XmlElement + private String antragstellerAnrede; + + @XmlElement + private String antragstellerVorname; + + @XmlElement + private String antragstellerNachname; + + @XmlElement + private String antragstellerStrasse; + + @XmlElement + private String antragstellerHausnummer; + + @XmlElement + private String antragstellerPlz; + + @XmlElement + private String antragstellerOrt; + + @XmlElementWrapper + @XmlElement(name = "nachricht") + private List<Nachricht> nachrichten; + + @Getter + @Builder + static class Nachricht { + + @XmlElement + private boolean isFirst; + + @XmlElement + private String subject; + + @XmlElement + private String mailBody; + + @XmlElement + private String createdBy; + + @XmlElement + private String createdAt; + + @XmlElementWrapper + @XmlElement(name = "attachment") + private List<String> attachments; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfService.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfService.java new file mode 100644 index 0000000000000000000000000000000000000000..c4117625a214ec046f0a3d4b2ac38bddbb24d0dd --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfService.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.time.format.DateTimeFormatter; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.postfach.PostfachMail.Direction; +import de.itvsh.goofy.vorgang.Antragsteller; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.kop.common.pdf.PdfService; + +@Service +class PostfachNachrichtPdfService { + + static final String PDF_TEMPLATE_PATH = "classpath:fop/postfach-nachrichten.xsl"; + + static final String SYSTEM_NACHRICHT_NAME = StringUtils.EMPTY; + static final String FALLBACK_USER_NAME = "Unbekannter Benutzer"; + + static final String FALLBACK_ANTRAGSTELLER_NAME = "Antragsteller"; + + // TODO Auf Konstante mit Locale umstellen + private static final DateTimeFormatter CREATED_AT_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy hh:mm:ss"); + + @Autowired + private PdfService pdfService; + + private boolean isFirstNachricht; + + @Value(PostfachNachrichtPdfService.PDF_TEMPLATE_PATH) + private Resource pdfTemplate; + + public OutputStream getAllAsPdf(VorgangWithEingang vorgang, Stream<PostfachNachrichtPdfData> postfachNachrichten, OutputStream out) { + return pdfService.createPdf(getTemplate(), out, buildModel(vorgang, postfachNachrichten)); + } + + InputStream getTemplate() { + try { + return pdfTemplate.getInputStream(); + } catch (IOException e) { + throw new TechnicalException("Pdf Template for postfach nachrichten could not be loaded", e); + } + } + + PostfachNachrichtPdfModel buildModel(VorgangWithEingang vorgang, Stream<PostfachNachrichtPdfData> postfachNachrichten) { + var pdfModelBuilder = PostfachNachrichtPdfModel.builder().vorgangNummer(vorgang.getNummer()).vorgangName(vorgang.getName()); + + Optional.ofNullable(vorgang.getEingang().getAntragsteller()).ifPresent(antragsteller -> mapAntragsteller(pdfModelBuilder, antragsteller)); + + mapNachrichten(pdfModelBuilder, postfachNachrichten, vorgang.getEingang().getAntragsteller()); + + return pdfModelBuilder.build(); + } + + void mapAntragsteller(PostfachNachrichtPdfModel.PostfachNachrichtPdfModelBuilder modelBuilder, Antragsteller antragsteller) { + modelBuilder.antragstellerAnrede(antragsteller.getAnrede()) + .antragstellerVorname(antragsteller.getVorname()) + .antragstellerNachname(antragsteller.getNachname()) + .antragstellerStrasse(antragsteller.getStrasse()) + .antragstellerHausnummer(antragsteller.getHausnummer()) + .antragstellerPlz(antragsteller.getPlz()) + .antragstellerOrt(antragsteller.getOrt()); + } + + private void mapNachrichten(PostfachNachrichtPdfModel.PostfachNachrichtPdfModelBuilder pdfModelBuilder, + Stream<PostfachNachrichtPdfData> postfachNachrichten, Antragsteller antragsteller) { + isFirstNachricht = true; + + pdfModelBuilder.nachrichten(postfachNachrichten.map(nachricht -> mapPostfachNachricht(nachricht, antragsteller)).toList()); + + } + + PostfachNachrichtPdfModel.Nachricht mapPostfachNachricht(PostfachNachrichtPdfData postfachMail, Antragsteller antragsteller) { + return PostfachNachrichtPdfModel.Nachricht.builder() + .isFirst(isFirstNachricht()) + .subject(postfachMail.getSubject()) + .mailBody(postfachMail.getMailBody()) + .createdAt(CREATED_AT_FORMATTER.format(postfachMail.getCreatedAt())) + .createdBy(buildAbsenderName(postfachMail, antragsteller)) + .attachments(postfachMail.getAttachmentNames()) + .build(); + } + + private boolean isFirstNachricht() { + if (isFirstNachricht) { + isFirstNachricht = false; + return true; + } + return false; + } + + String buildAbsenderName(PostfachNachrichtPdfData postfachNachrichtPdfData, Antragsteller antragsteller) { + if (postfachNachrichtPdfData.getDirection() == Direction.IN) { + return buildAbsenderForOutgoingNachricht(antragsteller); + } + return buildAbsenderForIncomingNachricht(postfachNachrichtPdfData); + } + + String buildAbsenderForOutgoingNachricht(Antragsteller antragsteller) { + return Optional.ofNullable(antragsteller) + .map(this::formatAntragstellerName) + .orElseGet(() -> FALLBACK_ANTRAGSTELLER_NAME); + } + + String buildAbsenderForIncomingNachricht(PostfachNachrichtPdfData postfachNachrichtPdfData) { + return Optional.ofNullable(postfachNachrichtPdfData.getUser()).map(user -> { + if (Objects.nonNull(user.getId()) && user.toString().startsWith(UserManagerUrlProvider.SYSTEM_USER_IDENTIFIER)) { + return SYSTEM_NACHRICHT_NAME; + } + return formatUserName(postfachNachrichtPdfData.getUser()); + }).orElseGet(() -> FALLBACK_USER_NAME); + } + + private String formatUserName(UserProfile user) { + return formatForPdf(user.getFirstName(), user.getLastName()); + } + + private String formatAntragstellerName(Antragsteller antragsteller) { + return formatForPdf(antragsteller.getVorname(), antragsteller.getNachname()); + } + + private String formatForPdf(String firstName, String lastName) { + return String.format("%s %s", getPdfStringValue(firstName), getPdfStringValue(lastName)).trim(); + } + + private String getPdfStringValue(String value) { + return Optional.ofNullable(StringUtils.trimToNull(value)).orElseGet(() -> StringUtils.EMPTY); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/postfach/ReplyOption.java b/goofy-server/src/main/java/de/itvsh/goofy/postfach/ReplyOption.java new file mode 100644 index 0000000000000000000000000000000000000000..aa447471baa157605aea5795ef7e9749c89a2cb4 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/postfach/ReplyOption.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +enum ReplyOption { + POSSIBLE, MANDATORY, FORBIDDEN; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/representation/RepresentationController.java b/goofy-server/src/main/java/de/itvsh/goofy/representation/RepresentationController.java new file mode 100644 index 0000000000000000000000000000000000000000..81d1af2bc141391fb3620cdf902bf31b10d921d7 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/representation/RepresentationController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.representation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.binaryfile.BinaryFileModelAssembler; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileService; + +@RestController +@RequestMapping(RepresentationController.REPRESENTATIONS_PATH) +public class RepresentationController { + + static final String REPRESENTATIONS_PATH = "/api/representations"; // NOSONAR + + static final String PARAM_EINGANG_ID = "eingangId"; + + @Autowired + private OzgFileService fileService; + @Autowired + private BinaryFileModelAssembler modelAssembler; + + @GetMapping(params = PARAM_EINGANG_ID) + public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { + return modelAssembler.toCollectionModel(fileService.getRepresentationsByEingang(eingangId)); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/system/PlutoSystemStatus.java b/goofy-server/src/main/java/de/itvsh/goofy/system/PlutoSystemStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..0bc1b50bf510e22678c6771d017aed78fb508356 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/system/PlutoSystemStatus.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import lombok.Builder; + +@Builder +public record PlutoSystemStatus(boolean isSearchServerAvailable, String javaVersion, String buildVersion) { +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/system/RemoteSystemStatusMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/system/RemoteSystemStatusMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..b5eea4fa81f4421272a25a4f48a4a7d2e045733c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/system/RemoteSystemStatusMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import de.itvsh.ozg.pluto.system.GrpcGetSystemStatusResponse; +import org.mapstruct.Mapper; + +@Mapper +interface RemoteSystemStatusMapper { + + PlutoSystemStatus fromGrpc(GrpcGetSystemStatusResponse systemStatusResponse); +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..178ecbd1128a8350ded564e3cc0ffef1fd337a18 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusRemoteService.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import de.itvsh.ozg.pluto.system.GrpcGetSystemStatusRequest; +import de.itvsh.ozg.pluto.system.SystemStatusServiceGrpc.SystemStatusServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +class SystemStatusRemoteService { + + @GrpcClient("pluto") + private SystemStatusServiceBlockingStub systemStatusBlockingStub; + @Autowired + private RemoteSystemStatusMapper remoteSystemStatusMapper; + + public PlutoSystemStatus getSystemStatus() { + var systemStatusResponse = systemStatusBlockingStub.getSystemStatus(GrpcGetSystemStatusRequest.newBuilder().build()); + return remoteSystemStatusMapper.fromGrpc(systemStatusResponse); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusService.java b/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusService.java new file mode 100644 index 0000000000000000000000000000000000000000..c2e24f5f9e101a699fa6a80bc37afa94275967f7 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/system/SystemStatusService.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SystemStatusService { + + @Autowired + private SystemStatusRemoteService systemStatusRemoteService; + + public boolean isSearchServerAvailable() { + return systemStatusRemoteService.getSystemStatus().isSearchServerAvailable(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Antragsteller.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Antragsteller.java new file mode 100644 index 0000000000000000000000000000000000000000..bbb0b3b6383b5fd503d8b05a10bcc32df05e621e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Antragsteller.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Map; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class Antragsteller { + + private String anrede; + private String nachname; + private String vorname; + private String geburtsdatum; + private String geburtsort; + private String geburtsname; + private String email; + private String telefon; + private String strasse; + private String hausnummer; + private String plz; + private String ort; + private String postfachId; + + private Map<String, Object> otherData; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AntragstellerMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AntragstellerMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..0f6d454c5c35d5438ea4ba12f63f3cea6b919aeb --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AntragstellerMapper.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.NullValuePropertyMappingStrategy; + +import de.itvsh.kop.pluto.common.grpc.GrpcFormDataMapper; +import de.itvsh.ozg.pluto.vorgang.GrpcAntragsteller; + +@Mapper(uses = { GrpcFormDataMapper.class }, // + nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, // + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) +interface AntragstellerMapper { + + Antragsteller toAntragsteller(GrpcAntragsteller antragsteller); +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AssignUserCommandBody.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AssignUserCommandBody.java new file mode 100644 index 0000000000000000000000000000000000000000..f1c7fce660b022810ab0d4768245df99c8f2f7f3 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/AssignUserCommandBody.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.command.CommandBody; +import de.itvsh.goofy.common.user.UserId; +import lombok.Getter; + +@Getter +public class AssignUserCommandBody implements CommandBody { + + @LinkedUserProfileResource + private UserId assignedTo; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ClientAttributeUtils.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ClientAttributeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..79cee65868070efc3e04ae220dee80e1b2fb2187 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ClientAttributeUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.List; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +final class ClientAttributeUtils { + public static Stream<GrpcClientAttributeValue> findByName(String attributeName, List<GrpcClientAttribute> clientAttributes) { + return clientAttributes.stream() + .filter(attribute -> StringUtils.equals(attribute.getAttributeName(), attributeName)) + .map(GrpcClientAttribute::getValue); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Eingang.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Eingang.java new file mode 100644 index 0000000000000000000000000000000000000000..52ee93023a3dc7573b0995319fbabb932d82e187 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Eingang.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Map; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@Builder +@ToString +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class Eingang { + + private String id; + + private int numberOfAttachments; + private int numberOfRepresentations; + + private ZustaendigeStelle zustaendigeStelle; + private Antragsteller antragsteller; + private EingangHeader header; + + @ToString.Exclude + private Map<String, Object> formData; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeader.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..8e80c0296fb404127b93cdfa5c52edd002ca363f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeader.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.ZonedDateTime; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class EingangHeader { + + private String requestId; + private ZonedDateTime createdAt; + private String formId; + private String formName; + private String sender; + private String customer; + private String customerId; + private String client; + private String clientId; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeaderMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeaderMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..49d096ffbf611ff78aef9217e8683a80c5ed3d61 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangHeaderMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.ZonedDateTime; + +import org.mapstruct.Mapper; + +import de.itvsh.ozg.pluto.vorgang.GrpcEingangHeader; + +@Mapper +interface EingangHeaderMapper { + + EingangHeader toEingangHeader(GrpcEingangHeader header); + + public static ZonedDateTime mapDateStrToZonedDateTime(String zdtStr) { + return ZonedDateTime.parse(zdtStr); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..91764d4ead04d2fd8c89efb55fceb3f52c2c8a73 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/EingangMapper.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import de.itvsh.kop.pluto.common.grpc.GrpcFormDataMapper; +import de.itvsh.ozg.pluto.vorgang.GrpcEingang; + +@Mapper(uses = { AntragstellerMapper.class, + EingangHeaderMapper.class, ZustaendigeStelleMapper.class, + GrpcFormDataMapper.class }, collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +interface EingangMapper { + + @Mapping(source = "formData", target = "formData") + Eingang fromGrpc(GrpcEingang eingang); + +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/FindVorgaengeHeaderRequestCriteria.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/FindVorgaengeHeaderRequestCriteria.java new file mode 100644 index 0000000000000000000000000000000000000000..91e7d6490e166aca78a5aae55364680236767fcf --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/FindVorgaengeHeaderRequestCriteria.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Optional; + +import de.itvsh.goofy.common.user.UserId; +import lombok.Builder; +import lombok.Getter; + +@Builder(toBuilder = true) +@Getter +class FindVorgaengeHeaderRequestCriteria { + private int page; + private Optional<Integer> requestLimit; + private int limit; + private int offset; + private Optional<String> searchBy; + private OrderBy orderBy; + private Optional<UserId> assignedTo; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/OrderBy.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/OrderBy.java new file mode 100644 index 0000000000000000000000000000000000000000..9f5058c8b0a6437b767dd479ec72d3f4772ea17c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/OrderBy.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +enum OrderBy { + PRIORITY, + EA_PRIORITY +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ResetNewPostfachNachrichtBody.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ResetNewPostfachNachrichtBody.java new file mode 100644 index 0000000000000000000000000000000000000000..0297ed07eed3f71889ded8c408710a90fefabd5a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ResetNewPostfachNachrichtBody.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +class ResetNewPostfachNachrichtBody { + private boolean hasNewPostfachNachricht; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgaengeHeaderResponse.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgaengeHeaderResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..070b530907b5a4bb64d2042567731ce59b193c1b --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgaengeHeaderResponse.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.List; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +class VorgaengeHeaderResponse { + + private List<VorgangHeader> vorgaengeHeader; + private long total; + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Vorgang.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Vorgang.java new file mode 100644 index 0000000000000000000000000000000000000000..50e976cb17b89f2ecd795c35e778ffb90bb3b679 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/Vorgang.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import de.itvsh.goofy.common.user.UserRole; + +public interface Vorgang { + + enum VorgangStatus { + NEU, ANGENOMMEN, VERWORFEN, IN_BEARBEITUNG, BESCHIEDEN, ABGESCHLOSSEN, WEITERGELEITET; + + private Map<String, Set<VorgangStatus>> allowedFollowStatusByRole = new HashMap<>(); + + static { + NEU.allowedFollowStatusByRole.put(UserRole.EINHEITLICHER_ANSPRECHPARTNER, Set.of(WEITERGELEITET, VERWORFEN)); + IN_BEARBEITUNG.allowedFollowStatusByRole.put(UserRole.EINHEITLICHER_ANSPRECHPARTNER, Set.of(ABGESCHLOSSEN, WEITERGELEITET)); + VERWORFEN.allowedFollowStatusByRole.put(UserRole.EINHEITLICHER_ANSPRECHPARTNER, Set.of(NEU)); + ABGESCHLOSSEN.allowedFollowStatusByRole.put(UserRole.EINHEITLICHER_ANSPRECHPARTNER, Set.of(IN_BEARBEITUNG)); + + NEU.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(ANGENOMMEN, VERWORFEN)); + VERWORFEN.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(NEU)); + ANGENOMMEN.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(IN_BEARBEITUNG)); + IN_BEARBEITUNG.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(BESCHIEDEN, ANGENOMMEN)); + BESCHIEDEN.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(ABGESCHLOSSEN, IN_BEARBEITUNG)); + ABGESCHLOSSEN.allowedFollowStatusByRole.put(UserRole.VERWALTUNG_USER, Set.of(IN_BEARBEITUNG)); + } + + public boolean isTransactionAllowed(String userRole, VorgangStatus newStatus) { + if (Objects.isNull(allowedFollowStatusByRole.get(userRole))) { + return false; + } + return allowedFollowStatusByRole.get(userRole).contains(newStatus); + } + } + + String getId(); + + long getVersion(); + + VorgangStatus getStatus(); + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangAuthorizationService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangAuthorizationService.java new file mode 100644 index 0000000000000000000000000000000000000000..8a42994afb9501211d2ce12d91d44f1ace1f0493 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangAuthorizationService.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +@Service +class VorgangAuthorizationService { + + static final String VERIFY_BY_USER_ROLE = "@vorgangAuthorizationService.verifyByUserRole(returnObject.getContent())"; + + @Autowired + private CurrentUserService userService; + + public boolean verifyByUserRole(VorgangWithEingang vorgang) { + if (userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)) { + return vorgang.getStatus() == VorgangStatus.NEU; + } + return true; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangController.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangController.java new file mode 100644 index 0000000000000000000000000000000000000000..a62fa05a4d234b2893240a0ee9f862ebbbf583f6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangController.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Optional; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.goofy.common.user.UserId; + +@RestController +@RequestMapping(VorgangController.PATH) +public class VorgangController { + + static final String PATH = "/api/vorgangs"; // NOSONAR + static final String PARAM_SEARCH = "searchBy"; + static final String PARAM_PAGE = "page"; + static final String PARAM_LIMIT = "limit"; + static final String PARAM_ASSIGNED_TO = "assignedTo"; + + static final int VORGANG_PAGE_SIZE = 100; + + @Autowired + private ClientAttributeService clientAttributeService; + @Autowired + private VorgangService vorgangService; + @Autowired + private VorgangModelAssembler modelAssembler; + + @GetMapping + public CollectionModel<EntityModel<Vorgang>> getVorgangListByPage(@RequestParam(defaultValue = "0") int page, + @RequestParam Optional<String> searchBy, @RequestParam Optional<Integer> limit, @RequestParam Optional<UserId> assignedTo) { + var requestCriteria = buildFindVorgaengeRequestCriteria(page, searchBy, limit, assignedTo); + + var vorgaengeHeaderResponse = vorgangService.findVorgaengeHeader(requestCriteria); + + return modelAssembler.toCollectionModel(vorgaengeHeaderResponse.getVorgaengeHeader().stream(), vorgaengeHeaderResponse, requestCriteria); + } + + FindVorgaengeHeaderRequestCriteria buildFindVorgaengeRequestCriteria(int page, Optional<String> searchBy, Optional<Integer> limit, + Optional<UserId> assignedTo) { + return FindVorgaengeHeaderRequestCriteria.builder() + .page(page) + .requestLimit(limit) + .limit(getLimitValue(limit)) + .offset(calculateOffset(page, limit)) + .searchBy(searchBy) + .assignedTo(assignedTo) + .build(); + } + + private int calculateOffset(final int page, final Optional<Integer> limit) { + return page * getLimitValue(limit); + } + + private int getLimitValue(Optional<Integer> limit) { + return limit.orElse(VORGANG_PAGE_SIZE); + } + + @PostAuthorize(VorgangAuthorizationService.VERIFY_BY_USER_ROLE) + @GetMapping("/{vorgangId}") + public EntityModel<Vorgang> getVorgangWithEingang(@PathVariable String vorgangId) { + return modelAssembler.toModel(getVorgang(vorgangId)); + } + + public VorgangWithEingang getVorgang(String vorgangId) { + return vorgangService.findVorgangWithEingang(vorgangId); + } + + @PutMapping(path = "/{vorgangId}/hasNewPostfachNachricht", consumes = MediaType.APPLICATION_JSON_VALUE) + public void resetNewPostfachNachricht(@PathVariable String vorgangId, @RequestBody ResetNewPostfachNachrichtBody body, + HttpServletResponse response) { + if (body.isHasNewPostfachNachricht()) { + response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); + return; + } + + clientAttributeService.resetPostfachNachricht(vorgangId); + response.setStatus(HttpStatus.NO_CONTENT.value()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeader.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..0aee2c0b3d84e66a771dddb632a6b22728f8ad38 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeader.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.LocalDate; +import java.time.ZonedDateTime; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.user.UserId; +import lombok.Builder; +import lombok.Getter; +import lombok.With; + +@Getter +@Builder +class VorgangHeader implements Vorgang { + + @JsonIgnore + private String id; + @JsonIgnore + private long version; + + @LinkedUserProfileResource + private UserId assignedTo; + + private String name; + private VorgangStatus status; + private ZonedDateTime createdAt; + private String aktenzeichen; + private String nummer; + + @With + @JsonProperty(access = Access.READ_ONLY) + private LocalDate nextFrist; + + private boolean hasPostfachNachricht; + + private boolean hasNewPostfachNachricht; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeaderMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeaderMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..b87f10b0283402e95d98b4979fe5c881a346dcab --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangHeaderMapper.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.LocalDate; +import java.util.List; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import de.itvsh.ozg.pluto.vorgang.GrpcVorgangHeader; + +@Mapper(uses = UserIdMapper.class, unmappedTargetPolicy = ReportingPolicy.WARN, collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +interface VorgangHeaderMapper { + + static final String WIEDERVORLAGE_NEXT_FRIST_ATTRIBUTE_NAME = "nextWiedervorlageFrist"; + static final String HAS_POSTFACH_NACHRICHT_ATTRIBUTE_NAME = "hasPostfachNachricht"; + static final String HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME = ClientAttributeService.HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME; + + @Mapping(target = "nextFrist", source = "clientAttributesList") + @Mapping(target = "hasPostfachNachricht", expression = "java(mapBoolClientAttributet(vorgangHeader.getClientAttributesList(), HAS_POSTFACH_NACHRICHT_ATTRIBUTE_NAME))") + @Mapping(target = "hasNewPostfachNachricht", expression = "java(mapBoolClientAttributet(vorgangHeader.getClientAttributesList(), HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME))") + VorgangHeader toVorgangHeader(GrpcVorgangHeader vorgangHeader); + + default LocalDate mapNextFrist(List<GrpcClientAttribute> clientAttributes) { + return ClientAttributeUtils.findByName(WIEDERVORLAGE_NEXT_FRIST_ATTRIBUTE_NAME, clientAttributes) + .map(GrpcClientAttributeValue::getStringValue) + .map(LocalDate::parse).findFirst().orElse(null); + } + + default boolean mapBoolClientAttributet(List<GrpcClientAttribute> clientAttributes, String attributeName) { + return ClientAttributeUtils.findByName(attributeName, clientAttributes) + .map(GrpcClientAttributeValue::getBoolValue) + .findFirst().orElse(false); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..3b29f1cfbc65d7908a03148bc2740aabb68c4182 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangModelAssembler.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.wiedervorlage.WiedervorlageController; + +@Component +class VorgangModelAssembler implements RepresentationModelAssembler<Vorgang, EntityModel<Vorgang>> { + + static final String REL_NEXT = "next"; + static final String REL_PREVIOUS = "previous"; + static final String REL_VORGANG_MIT_EINGANG = "vorgang-mit-eingang"; + static final String REL_WIEDERVORLAGEN = "wiedervorlagen"; + static final String REL_VORGANG_ASSIGN = "assign"; + + @Autowired + private CurrentUserService userService; + + public CollectionModel<EntityModel<Vorgang>> toCollectionModel(Stream<? extends VorgangHeader> entities, VorgaengeHeaderResponse response, + FindVorgaengeHeaderRequestCriteria requestCriteria) { + // FIXME selfLink + var model = CollectionModel.of(entities.map(this::toModel).toList(), linkTo(VorgangController.class).withSelfRel()); + + var currentPage = requestCriteria.getPage(); + var lastEntryIndex = getLastEntryIndex(currentPage, response.getVorgaengeHeader().size(), requestCriteria.getLimit()); + + getPreviousPage(currentPage).ifPresent(page -> model.add(buildPageLink(page, REL_PREVIOUS, requestCriteria))); + getNextPage(response.getTotal(), lastEntryIndex, currentPage).ifPresent(page -> model.add(buildPageLink(page, REL_NEXT, requestCriteria))); + + return model; + } + + int getLastEntryIndex(int currentPage, int currentPageSize, int pageSize) { + if (isFirstPage(currentPage)) { + return currentPageSize; + } + return currentPageSize + (currentPage * pageSize); + } + + Optional<Integer> getPreviousPage(int currentPage) { + if (isFirstPage(currentPage)) { + return Optional.empty(); + } + return Optional.of(currentPage - 1); + } + + private boolean isFirstPage(int currentPage) { + return currentPage == 0; + } + + Optional<Integer> getNextPage(long total, int lastEntryIndex, int currentPage) { + if (total > lastEntryIndex) { + return Optional.of(currentPage + 1); + } + return Optional.empty(); + } + + private Link buildPageLink(int page, String linkRel, FindVorgaengeHeaderRequestCriteria requestCriteria) { + return createLink(page, requestCriteria).withRel(linkRel); + } + + private Link createLink(int page, FindVorgaengeHeaderRequestCriteria requestCriteria) { + var uriBuilder = createUriBuilder(page); + addSearchParameter(requestCriteria, uriBuilder); + addLimitParameter(requestCriteria, uriBuilder); + addAssignedToParameter(requestCriteria, uriBuilder); + return Link.of(uriBuilder.build().toUri().toASCIIString()); + } + + UriComponentsBuilder createUriBuilder(int page) { + return linkTo(VorgangController.class).toUriComponentsBuilder().queryParam(VorgangController.PARAM_PAGE, page); + } + + void addLimitParameter(FindVorgaengeHeaderRequestCriteria requestCriteria, UriComponentsBuilder uriBuilder) { + requestCriteria.getRequestLimit().ifPresent(limit -> uriBuilder.queryParam(VorgangController.PARAM_LIMIT, limit)); + } + + void addSearchParameter(FindVorgaengeHeaderRequestCriteria requestCriteria, UriComponentsBuilder uriBuilder) { + requestCriteria.getSearchBy().ifPresent(searchBy -> uriBuilder.queryParam(VorgangController.PARAM_SEARCH, searchBy)); + } + + void addAssignedToParameter(FindVorgaengeHeaderRequestCriteria requestCriteria, UriComponentsBuilder uriBuilder) { + requestCriteria.getAssignedTo().ifPresent(assignedTo -> uriBuilder.queryParam(VorgangController.PARAM_ASSIGNED_TO, assignedTo)); + } + + @Override + public EntityModel<Vorgang> toModel(Vorgang vorgang) { + var selfLinkBuilder = linkTo(VorgangController.class).slash(vorgang.getId()); + var assignLink = linkTo(methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), + vorgang.getVersion(), null)).withRel(REL_VORGANG_ASSIGN); + var wiedervorlagenLink = linkTo(methodOn(WiedervorlageController.class).findByVorgang(vorgang.getId())).withRel(REL_WIEDERVORLAGEN); + + return ModelBuilder.fromEntity(vorgang) + .addLink(selfLinkBuilder.withSelfRel()) + .addLink(selfLinkBuilder.withRel(REL_VORGANG_MIT_EINGANG)) + .ifMatch(this::isVerwaltung).addLink(assignLink) + .ifMatch(() -> userService.hasRole(UserRole.VERWALTUNG_USER)).addLink(wiedervorlagenLink) + .buildModel(); + } + + private boolean isVerwaltung() { + return userService.hasRole(UserRole.VERWALTUNG_USER) || userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..0e0f3d1baf9e77999c732148fe2951d238a55d70 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangRemoteService.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.ozg.pluto.vorgang.GrpcFilterBy; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangRequest; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangRequest.GrpcOrderBy; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangResponse; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangWithEingangRequest; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangWithEingangResponse; +import de.itvsh.ozg.pluto.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class VorgangRemoteService { + + @GrpcClient("pluto") + private VorgangServiceBlockingStub vorgangServiceStub; + @Autowired + private VorgangHeaderMapper vorgangHeaderMapper; + @Autowired + private VorgangWithEingangMapper vorgangWithEingangMapper; + @Autowired + private CurrentUserService userService; + + public VorgaengeHeaderResponse findVorgaengeHeader(FindVorgaengeHeaderRequestCriteria requestCriteria) { + var request = buildFindVorgangRequest(requestCriteria); + + var response = vorgangServiceStub.findVorgang(request); + + return buildVorgaengeHeaderResponse(response); + } + + GrpcFindVorgangRequest buildFindVorgangRequest(FindVorgaengeHeaderRequestCriteria requestCriteria) { + var requestBuilder = createFindVorgangRequestBuilder(requestCriteria); + + requestCriteria.getSearchBy().ifPresent(requestBuilder::setSearchBy); + + return requestBuilder.build(); + } + + private GrpcFindVorgangRequest.Builder createFindVorgangRequestBuilder(FindVorgaengeHeaderRequestCriteria requestCriteria) { + return GrpcFindVorgangRequest.newBuilder() + .setOrderBy(GrpcOrderBy.valueOf(requestCriteria.getOrderBy().name())) + .setLimit(requestCriteria.getLimit()) + .setOffset(requestCriteria.getOffset()) + .setFilterBy(createFilterBy(requestCriteria.getAssignedTo())); + } + + VorgaengeHeaderResponse buildVorgaengeHeaderResponse(GrpcFindVorgangResponse response) { + return VorgaengeHeaderResponse.builder() + .total(response.getTotal()) + .vorgaengeHeader(getVorgaengeHeaderFromResponse(response)) + .build(); + } + + private List<VorgangHeader> getVorgaengeHeaderFromResponse(GrpcFindVorgangResponse response) { + return response.getVorgangList().stream() + .map(vorgangHeaderMapper::toVorgangHeader) + .collect(Collectors.toList()); + } + + public VorgangWithEingang findVorgangWithEingang(String vorgangId) { + var request = buildFindVorgangWithEingangRequest(vorgangId); + + var response = vorgangServiceStub.findVorgangWithEingang(request); + + return buildVorgangWithEingangResponse(response); + } + + GrpcFindVorgangWithEingangRequest buildFindVorgangWithEingangRequest(String vorgangId) { + return GrpcFindVorgangWithEingangRequest.newBuilder() + .setId(vorgangId) + .setFilterBy(createFilterBy(Optional.empty())) + .build(); + } + + GrpcFilterBy createFilterBy(Optional<UserId> assignedTo) { + var builder = GrpcFilterBy.newBuilder(); + assignedTo.ifPresent(assignToId -> builder.setAssignedTo(assignToId.toString())); + + if (userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)) { + return buildFilterByForPoststelle(builder); + } + if (userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)) { + return buildFilterByForEa(builder); + } + + return buildFilterByForOtherRoles(builder); + } + + GrpcFilterBy buildFilterByForPoststelle(GrpcFilterBy.Builder builder) { + return builder + .setFilterByOrganisationseinheitenId(false) + .addStatus(VorgangStatus.NEU.name()) + .build(); + } + + private GrpcFilterBy buildFilterByForEa(GrpcFilterBy.Builder builder) { + return builder + .setFilterByOrganisationseinheitenId(false) + .build(); + } + + private GrpcFilterBy buildFilterByForOtherRoles(GrpcFilterBy.Builder builder) { + return builder + .setFilterByOrganisationseinheitenId(true) + .addAllOrganisationseinheitId(getOrganisationseinheitenIdsFromUser()) + .build(); + } + + Collection<String> getOrganisationseinheitenIdsFromUser() { + return userService.getUser().getOrganisationseinheitIds(); + } + + private VorgangWithEingang buildVorgangWithEingangResponse(GrpcFindVorgangWithEingangResponse response) { + return vorgangWithEingangMapper.toVorgangWithEingang(response.getVorgangWithEingang()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangService.java new file mode 100644 index 0000000000000000000000000000000000000000..8cb7e2648e4ae80da5ccc0da17b5aa0bf32cc3ff --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangService.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; + +@Service +class VorgangService { + + @Autowired + private VorgangRemoteService remoteService; + @Autowired + private CurrentUserService userService; + + public VorgaengeHeaderResponse findVorgaengeHeader(FindVorgaengeHeaderRequestCriteria requestCriteria) { + requestCriteria = setOrderBy(requestCriteria); + + return remoteService.findVorgaengeHeader(requestCriteria); + } + + FindVorgaengeHeaderRequestCriteria setOrderBy(FindVorgaengeHeaderRequestCriteria requestCriteria) { + if (userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)) { + return requestCriteria.toBuilder().orderBy(OrderBy.EA_PRIORITY).build(); + } else { + return requestCriteria.toBuilder().orderBy(OrderBy.PRIORITY).build(); + } + } + + public VorgangWithEingang findVorgangWithEingang(String vorgangId) { + return remoteService.findVorgangWithEingang(vorgangId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingang.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingang.java new file mode 100644 index 0000000000000000000000000000000000000000..cd4d4598ab09d4882251fb9f71f9de3f3387f3bb --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingang.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.ZonedDateTime; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.user.UserId; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class VorgangWithEingang implements Vorgang { + + @JsonIgnore + private String id; + @JsonIgnore + private long version; + + @LinkedUserProfileResource + private UserId assignedTo; + + private String name; + private VorgangStatus status; + private ZonedDateTime createdAt; + private String aktenzeichen; + private String nummer; + private boolean hasNewPostfachNachricht; + + private Eingang eingang; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..9a68c842f0e371bb75e0c3a34e482b36d22633e1 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapper.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import de.itvsh.goofy.common.BaseTypesMapper; +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import de.itvsh.ozg.pluto.vorgang.GrpcVorgangWithEingang; + +@Mapper(uses = { EingangMapper.class, UserIdMapper.class, BaseTypesMapper.class }) +interface VorgangWithEingangMapper { + + @Mapping(target = "hasNewPostfachNachricht", source = "clientAttributesList") + VorgangWithEingang toVorgangWithEingang(GrpcVorgangWithEingang vorgangMitEingang); + + default boolean mapHasNewPostfachNachricht(List<GrpcClientAttribute> clientAttributes) { + return ClientAttributeUtils.findByName(ClientAttributeService.HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME, clientAttributes) + .map(GrpcClientAttributeValue::getBoolValue) + .findFirst().orElse(false); + } + +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessor.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessor.java new file mode 100644 index 0000000000000000000000000000000000000000..fa08d2876f97c02484df6fbf5399ed356ab001c8 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessor.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Objects; +import java.util.function.Predicate; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelProcessor; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.attachment.AttachmentController; +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.historie.HistorieController; +import de.itvsh.goofy.kommentar.KommentarController.KommentarByVorgangController; +import de.itvsh.goofy.postfach.PostfachMailController; +import de.itvsh.goofy.representation.RepresentationController; +import de.itvsh.goofy.vorgang.forwarding.ForwardingController; + +@Component +class VorgangWithEingangProzessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { + + static final LinkRelation REL_KOMMENTARE = LinkRelation.of("kommentare"); + static final LinkRelation REL_ATTACHMENTS = LinkRelation.of("attachments"); + static final LinkRelation REL_REPRESENTATIONS = LinkRelation.of("representations"); + static final LinkRelation REL_POSTFACH_MAILS = LinkRelation.of("postfachMails"); + static final LinkRelation REL_VORGANG_FORWARDING = LinkRelation.of("forwarding"); + static final LinkRelation REL_HISTORIE = LinkRelation.of("historie"); + + @Autowired + private PostfachMailController postfachMailController; + @Autowired + private CurrentUserService userService; + + private static final Predicate<VorgangWithEingang> HAS_ATTACHMENTS = vorgangWithEingang -> vorgangWithEingang.getEingang() + .getNumberOfAttachments() > 0; + + private static final Predicate<VorgangWithEingang> HAS_REPRESENTATIONS = vorgangWithEingang -> vorgangWithEingang.getEingang() + .getNumberOfRepresentations() > 0; + + @Override + public EntityModel<VorgangWithEingang> process(EntityModel<VorgangWithEingang> model) { + var vorgang = model.getContent(); + if (Objects.isNull(vorgang)) { + return model; + } + return ModelBuilder.fromModel(model) + .addLink(linkTo(KommentarByVorgangController.class).slash(vorgang.getId()).slash("kommentars").withRel(REL_KOMMENTARE)) + .ifMatch(HAS_ATTACHMENTS) + .addLink(vorgangWithEingang -> linkTo(methodOn(AttachmentController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) + .withRel(REL_ATTACHMENTS)) + .ifMatch(HAS_REPRESENTATIONS) + .addLink(vorgangWithEingang -> linkTo( + methodOn(RepresentationController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) + .withRel(REL_REPRESENTATIONS)) + .ifMatch(this::isPostfachConfigured) + .addLink(linkTo(methodOn(PostfachMailController.class).getAll(vorgang.getId())).withRel(REL_POSTFACH_MAILS)) + .ifMatch(this::isEinheitlicherAnsprechpartner) + .addLink(linkTo(methodOn(ForwardingController.class).findByVorgangId(vorgang.getId())).withRel(REL_VORGANG_FORWARDING)) + .addLink(linkTo(methodOn(HistorieController.class).getHistorieList(vorgang.getId())).withRel(REL_HISTORIE)) + .buildModel(); + } + + private boolean isPostfachConfigured(VorgangWithEingang vorgang) { + return postfachMailController.isPostfachConfigured(); + } + + private boolean isEinheitlicherAnsprechpartner(VorgangWithEingang vorgang) { + return userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangResponse.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..957b6ab30a1f731aa42f4372c8d865157a661643 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/VorgangWithEingangResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class VorgangWithEingangResponse { + private VorgangWithEingang vorgangWithEingang; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelle.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelle.java new file mode 100644 index 0000000000000000000000000000000000000000..3c46aff605d6f27aa0f4709e712f45d85149729a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelle.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class ZustaendigeStelle { + + private String organisationseinheitenId; + private String email; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..a83f60b09c5a31235c1f3e3c6221dc69280af583 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.ozg.pluto.vorgang.GrpcZustaendigeStelle; +import org.mapstruct.Mapper; + +@Mapper +interface ZustaendigeStelleMapper { + + ZustaendigeStelle mapToZustaendigeStelle(GrpcZustaendigeStelle grpcZustaendigeStelle); +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessor.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessor.java new file mode 100644 index 0000000000000000000000000000000000000000..868f2bd8cabd7b11557dbcc0c73cb61098e970b2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessor.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.command; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Objects; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelProcessor; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +@Component +class VorgangCommandProzessor implements RepresentationModelProcessor<EntityModel<? extends Vorgang>> { + + static final LinkRelation REL_VORGANG_ANNEHMEN = LinkRelation.of("annehmen"); + static final LinkRelation REL_VORGANG_VERWERFEN = LinkRelation.of("verwerfen"); + static final LinkRelation REL_VORGANG_ZURUECKHOLEN = LinkRelation.of("zurueckholen"); + static final LinkRelation REL_VORGANG_BEARBEITEN = LinkRelation.of("bearbeiten"); + static final LinkRelation REL_VORGANG_BESCHEIDEN = LinkRelation.of("bescheiden"); + static final LinkRelation REL_VORGANG_ZURUECKSTELLEN = LinkRelation.of("zurueckstellen"); + static final LinkRelation REL_VORGANG_ABSCHLIESSEN = LinkRelation.of("abschliessen"); + static final LinkRelation REL_VORGANG_WIEDEREROEFFNEN = LinkRelation.of("wiedereroeffnen"); + + @Autowired + private CurrentUserService userService; + + @Override + public EntityModel<? extends Vorgang> process(EntityModel<? extends Vorgang> model) { + var vorgang = model.getContent(); + + if (Objects.isNull(vorgang)) { + return model; + } + + addLinkIfTransactionAllowed(VorgangStatus.ANGENOMMEN, getRelationForAngenommenByStatus(vorgang), vorgang, model); + addLinkIfTransactionAllowed(VorgangStatus.VERWORFEN, REL_VORGANG_VERWERFEN, vorgang, model); + addLinkIfTransactionAllowed(VorgangStatus.NEU, REL_VORGANG_ZURUECKHOLEN, vorgang, model); + addLinkIfTransactionAllowed(VorgangStatus.IN_BEARBEITUNG, getRelationForInBearbeitungByStatus(vorgang), vorgang, model); + addLinkIfTransactionAllowed(VorgangStatus.BESCHIEDEN, REL_VORGANG_BESCHEIDEN, vorgang, model); + addLinkIfTransactionAllowed(VorgangStatus.ABGESCHLOSSEN, REL_VORGANG_ABSCHLIESSEN, vorgang, model); + + return model; + } + + private LinkRelation getRelationForAngenommenByStatus(Vorgang vorgang) { + if (vorgang.getStatus() == VorgangStatus.NEU) { + return REL_VORGANG_ANNEHMEN; + } else { + return REL_VORGANG_ZURUECKSTELLEN; + } + } + + private LinkRelation getRelationForInBearbeitungByStatus(Vorgang vorgang) { + if (vorgang.getStatus() == VorgangStatus.ANGENOMMEN) { + return REL_VORGANG_BEARBEITEN; + } else { + return REL_VORGANG_WIEDEREROEFFNEN; + } + } + + private void addLinkIfTransactionAllowed(VorgangStatus newStatus, LinkRelation relation, Vorgang vorgang, + EntityModel<? extends Vorgang> model) { + if (isTransactonAllowed(vorgang, newStatus)) { + model.add(linkTo( + methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(), null)) + .withRel(relation)); + } + } + + private boolean isTransactonAllowed(Vorgang vorgang, VorgangStatus newStatus) { + return vorgang.getStatus().isTransactionAllowed(getUserRole(), newStatus); + } + + String getUserRole() { + String role; + if (userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)) { + role = UserRole.EINHEITLICHER_ANSPRECHPARTNER; + } else if (userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)) { + role = UserRole.VERWALTUNG_POSTSTELLE; + } else { + role = UserRole.VERWALTUNG_USER; + } + + return role; + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessor.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessor.java new file mode 100644 index 0000000000000000000000000000000000000000..7f39316ea957fa2ceef9a815b34581a88d15173c --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessor.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.command; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Objects; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelProcessor; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.goofy.vorgang.forwarding.ForwardingController; + +@Component +public class VorgangWithEingangCommandProzessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { + + static final LinkRelation REL_VORGANG_FORWARD = LinkRelation.of("forward"); + static final LinkRelation REL_PENDING_COMMANDS = LinkRelation.of("pending-commands"); + + @Autowired + private CurrentUserService userService; + @Autowired + private ForwardingController forwardingController; + @Autowired + private CommandController commandController; + + @Override + public EntityModel<VorgangWithEingang> process(EntityModel<VorgangWithEingang> model) { + var vorgang = model.getContent(); + if (Objects.isNull(vorgang)) { + return model; + } + return ModelBuilder.fromModel(model) + .ifMatch(this::isForwardingAllowed) + .addLink(linkTo( + methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(), + null)).withRel(REL_VORGANG_FORWARD)) + .ifMatch(this::existsPendingCommands) + .addLink(linkTo(methodOn(CommandController.class).getPendingCommands(true, vorgang.getId())).withRel(REL_PENDING_COMMANDS)) + .buildModel(); + } + + boolean isForwardingAllowed(VorgangWithEingang vorgang) { + return userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER) + && (vorgang.getStatus() == VorgangStatus.NEU + || (vorgang.getStatus() == VorgangStatus.IN_BEARBEITUNG + && hasForwardingFailed(vorgang))); + } + + private boolean hasForwardingFailed(VorgangWithEingang vorgang) { + return !forwardingController.findFailedForwardings(vorgang.getId()).isEmpty(); + } + + private boolean existsPendingCommands(VorgangWithEingang vorgang) { + return commandController.existsPendingCommands(vorgang.getId()); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/Forwarding.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/Forwarding.java new file mode 100644 index 0000000000000000000000000000000000000000..74b6ba1f25ea904c27cabd197c97cfe33c549f1e --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/Forwarding.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.time.ZonedDateTime; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class Forwarding { + + public enum Status { + NEW, SENT, SUCCESSFULL, SEND_ERROR, FAILED + } + + @JsonIgnore + private String id; + @JsonIgnore + private String vorgangId; + @JsonIgnore + private Status status; + + private String zustaendigeStelle; + + private ZonedDateTime createdAt; + private String createdBy; + private String createdByName; + + private ZonedDateTime sentAt; + + private String errorMessage; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingController.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingController.java new file mode 100644 index 0000000000000000000000000000000000000000..298f36ed3084fd14959354b2e345e2d3fa8f5d07 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingController.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.vorgang.forwarding.Forwarding.Status; + +@RestController +@RequestMapping(ForwardingController.LIST_PATH) +public class ForwardingController { + + static final String LIST_PATH = "/api/forwardings"; // NOSONAR + + static final String PARAM_VORGANG_ID = "vorgangId"; + + @Autowired + private ForwardingService service; + @Autowired + private ForwardingModelAssembler modelAssembler; + + @GetMapping(params = { PARAM_VORGANG_ID }) + public CollectionModel<EntityModel<Forwarding>> findByVorgangId(@RequestParam String vorgangId) { + return modelAssembler.toCollectionModel(findByVorgang(vorgangId)); + } + + public List<Forwarding> findFailedForwardings(String vorgangId) { + return findByVorgang(vorgangId) + .filter(forwarding -> forwarding.getStatus() == Status.FAILED || forwarding.getStatus() == Status.SEND_ERROR) + .collect(Collectors.toList()); + } + + public Stream<Forwarding> findByVorgang(String vorgangId) { + return service.findByVorgang(vorgangId); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoService.java new file mode 100644 index 0000000000000000000000000000000000000000..1b0e313e8d8bbac81919a1b3642e5353d0dbe178 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoService.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Service +class ForwardingLandesnetzInfoService { + + @Autowired(required = false) + private LandesnetzInfoReadService readService; + + private Set<String> landesnetzInfo = Collections.emptySet(); + + @PostConstruct + private void initLandesnetzList() { + if (Objects.isNull(readService)) { + LOG.info("Landesnetz-Info not configured."); + return; + } + + readService.readLandesnetzInfo().thenAccept(this::assingInfos); + } + + private void assingInfos(Set<String> lnInfos) { + this.landesnetzInfo = lnInfos; + LOG.info("{} Landesnetz-Infos entries read.", landesnetzInfo.size()); + } + + public boolean isEmailInLandesnetz(String email) { + return isLandesnetzIncludingDomain(getDomain(email)); + } + + private String getDomain(String email) { + return email.substring(email.lastIndexOf('@') + 1, email.length()); + } + + private boolean isLandesnetzIncludingDomain(String emailDomain) { + return landesnetzInfo.contains(emailDomain) || + landesnetzInfo.stream().anyMatch(lnInfo -> isDomainMatching(lnInfo, emailDomain)); + } + + private boolean isDomainMatching(String netzDomain, String emailDomain) { + return netzDomain.startsWith(".") && emailDomain.endsWith(netzDomain); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7ede4790ca7a692b76af85738c54a5d17417556d --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingMapper.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import org.mapstruct.Mapper; + +import de.itvsh.goofy.common.TimeMapper; +import de.itvsh.ozg.pluto.forwarding.GrpcForwarding; + +@Mapper(uses = TimeMapper.class) +public interface ForwardingMapper { + + Forwarding fromGrpc(GrpcForwarding forwarding); +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..fc031e6da9c601d8eddf68cc8151f09f0f2e600f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssembler.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.vorgang.forwarding.Forwarding.Status; + +@Component +class ForwardingModelAssembler implements RepresentationModelAssembler<Forwarding, EntityModel<Forwarding>> { + + static final LinkRelation REL_MARK_AS_SUCCESS = LinkRelation.of("mark-as-success"); + static final LinkRelation REL_MARK_AS_FAIL = LinkRelation.of("mark-as-fail"); + + static final LinkRelation REL_SUCCESSFULL = LinkRelation.of("successfull"); + static final LinkRelation REL_FAILED = LinkRelation.of("failed"); + + static final LinkRelation REL_ERROR = LinkRelation.of("error"); + + @Override + public EntityModel<Forwarding> toModel(Forwarding entity) { + var selfLink = linkTo(ForwardingController.class).slash(entity.getId()); + + return ModelBuilder.fromEntity(entity) + .addLink(linkTo(ForwardingController.class).slash(entity.getId()).withSelfRel()) + .ifMatch(forwarding -> forwarding.getStatus() == Status.SENT) + .addLinks(buildMarkAsSuccessLink(entity)) + .ifMatch(forwarding -> forwarding.getStatus() == Status.SENT || forwarding.getStatus() == Status.SUCCESSFULL) + .addLinks(buildMarkAsFailLink(entity)) + .ifMatch(forwarding -> forwarding.getStatus() == Status.FAILED) + .addLink(selfLink.withRel(REL_FAILED)) + .ifMatch(forwarding -> forwarding.getStatus() == Status.SUCCESSFULL) + .addLink(selfLink.withRel(REL_SUCCESSFULL)) + .ifMatch(forwarding -> forwarding.getStatus() == Status.SEND_ERROR) + .addLink(selfLink.withRel(REL_ERROR)) + .buildModel(); + } + + public CollectionModel<EntityModel<Forwarding>> toCollectionModel(Stream<Forwarding> entities) { + return CollectionModel.of(entities.map(this::toModel).collect(Collectors.toList()), + linkTo(ForwardingController.class).withSelfRel()); + } + + public Link buildMarkAsSuccessLink(Forwarding forwarding) { + return buildLinkWithLinkRel(forwarding, REL_MARK_AS_SUCCESS); + } + + public Link buildMarkAsFailLink(Forwarding forwarding) { + return buildLinkWithLinkRel(forwarding, REL_MARK_AS_FAIL); + } + + private Link buildLinkWithLinkRel(Forwarding forwarding, LinkRelation linkRel) { + return linkTo(methodOn(CommandByRelationController.class).createCommand(forwarding.getVorgangId(), forwarding.getId(), -1, null)) + .withRel(linkRel); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordSizeConstraint.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordSizeConstraint.java new file mode 100644 index 0000000000000000000000000000000000000000..d02f76a21571755587b7c53d96d1bf2141c9fb9a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordSizeConstraint.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import de.itvsh.goofy.common.ValidationMessageCodes; + +@Retention(RUNTIME) +@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE }) +@Constraint(validatedBy = { ForwardingPasswordValidator.class }) +@Documented +public @interface ForwardingPasswordSizeConstraint { + String message() default ValidationMessageCodes.FIELD_SIZE; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; + + int max() default 40; + + int min() default 8; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidator.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..fb27a9c3d1a11ed674bd4db53196d485cd4cc4f1 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidator.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import de.itvsh.goofy.common.ValidationMessageCodes; + +class ForwardingPasswordValidator implements ConstraintValidator<ForwardingPasswordSizeConstraint, RedirectRequest> { + + @Autowired + private ForwardingLandesnetzInfoService landesnetzInfoService; + + @Override + public boolean isValid(RedirectRequest request, ConstraintValidatorContext context) { + if (StringUtils.isNotBlank(request.getEmail()) && isNotInLandesnetz(request.getEmail())) { + prepareConstraint(context); + + return isValidPassword(request); + } + return true; + } + + private boolean isNotInLandesnetz(String email) { + return !landesnetzInfoService.isEmailInLandesnetz(email); + } + + boolean isValidPassword(RedirectRequest request) { + var password = request.getPassword(); + var constraint = request.getClass().getAnnotation(ForwardingPasswordSizeConstraint.class); + + return ArrayUtils.isNotEmpty(password) && password.length >= constraint.min() && password.length <= constraint.max(); + } + + void prepareConstraint(ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ValidationMessageCodes.FIELD_SIZE) + .addPropertyNode("password").addConstraintViolation(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..00267c59331749e1b5d4e4e77f0aad9ce37df399 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteService.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.ozg.pluto.forwarding.ForwardingServiceGrpc.ForwardingServiceBlockingStub; +import de.itvsh.ozg.pluto.forwarding.GrpcFindForwardingsRequest; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +public class ForwardingRemoteService { + + @GrpcClient("pluto") + private ForwardingServiceBlockingStub serviceStub; + + @Autowired + private ForwardingMapper mapper; + + @Autowired + private ContextService contextService; + + public Stream<Forwarding> findForwardings(String vorgangId) { + var response = serviceStub.findForwardings(buildFindForwardingsRequest(vorgangId)); + + return response.getForwardingsList().stream().map(mapper::fromGrpc); + } + + private GrpcFindForwardingsRequest buildFindForwardingsRequest(String vorgangId) { + return GrpcFindForwardingsRequest.newBuilder() + .setContext(contextService.createCallContext()) + .setVorgangId(vorgangId) + .build(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingService.java new file mode 100644 index 0000000000000000000000000000000000000000..80b6708d4efeace04bba90b12cd14925de2f4777 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/ForwardingService.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +class ForwardingService { + + @Autowired + private ForwardingRemoteService remoteService; + + public Stream<Forwarding> findByVorgang(String vorgangId) { + return remoteService.findForwardings(vorgangId); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoProperties.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..fa3546749ef3226b48310f8f1d576b50c638805d --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = LandesnetzInfoProperties.LNINFO_CONFIG_PREFIX) +public class LandesnetzInfoProperties { + + static final String LNINFO_CONFIG_PREFIX = "kop.forwarding.lninfo"; + + private Resource url; +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadService.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadService.java new file mode 100644 index 0000000000000000000000000000000000000000..5cd49f758089dbb21ec299a0fc77da45e5ecb82f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadService.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.StreamUtils; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import lombok.extern.log4j.Log4j2; + +@Log4j2 +@Service +@ConditionalOnProperty(prefix = LandesnetzInfoProperties.LNINFO_CONFIG_PREFIX, name = { "url" }) +class LandesnetzInfoReadService { + + private static final String LANDESNETZ_TABLE_IDENTIFIER = "tbl_domains"; + private static final String LNSH = "LNSH"; + private static final String LNSH_ALTUMGEBUNG = "LNSH Altumgebung"; + + @Autowired + private LandesnetzInfoProperties properties; + + @Async + public CompletableFuture<Set<String>> readLandesnetzInfo() { + try { + return getResourceAsString() + .map(this::parseToDocument) + .map(this::parseTableEntries) + .filter(Objects::nonNull) + .map(CompletableFuture::completedFuture) + .orElseGet(() -> { + LOG.warn("No Landesnetz-Information found"); + return CompletableFuture.completedFuture(Collections.emptySet()); + }); + } catch (RuntimeException e) { + LOG.error("Error on reading Landesnetz-Infos.", e); + } + return CompletableFuture.completedFuture(Collections.emptySet()); + } + + private Optional<String> getResourceAsString() { + return getInputStream().map(this::copyToString); + } + + private Optional<InputStream> getInputStream() { + try { + return Optional.ofNullable(properties.getUrl().getInputStream()); + } catch (IOException e) { + throw new TechnicalException("Error on reading Landesnetz-Infos", e); + } + } + + private String copyToString(InputStream stream) { + try { + return StreamUtils.copyToString(stream, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new TechnicalException("Error on reading Landesnetz-Infos", e); + } + } + + private Document parseToDocument(String source) { + return Jsoup.parse(source); + } + + public Set<String> parseTableEntries(Document resourceDoc) { + return parseTable(resourceDoc); + } + + private Set<String> parseTable(Document doc) { + return getTableColumnValues(doc).stream() + .filter(this::isTableEntry) + .filter(column -> isValid(getSecondColumn(column))) + .map(this::getFirstColumn) + .collect(Collectors.toUnmodifiableSet()); + } + + private List<Element> getTableColumnValues(Document doc) { + return doc.select("#" + LANDESNETZ_TABLE_IDENTIFIER + " tr"); + } + + private boolean isValid(String value) { + return isLnsh(value) || isLnshAltumgebung(value); + } + + private boolean isLnsh(String value) { + return StringUtils.equals(value, LNSH); + } + + private boolean isLnshAltumgebung(String value) { + return StringUtils.equals(value, LNSH_ALTUMGEBUNG); + } + + private boolean isTableEntry(Element element) { + return !isHeaderEntry(element) && !element.childNodes().isEmpty(); + } + + private boolean isHeaderEntry(Node element) { + return StringUtils.equals(element.attr("class"), "header"); + } + + private String getSecondColumn(Element element) { + return selectTextFromTdElement(element, 1); + } + + private String getFirstColumn(Element element) { + return selectTextFromTdElement(element, 0); + } + + private String selectTextFromTdElement(Element element, int index) { + return element.select("td").get(index).text(); + } +} diff --git a/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/RedirectRequest.java b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/RedirectRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..e39fa3627da588ab9672b590f51644e74382a90d --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/vorgang/forwarding/RedirectRequest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static de.itvsh.goofy.common.ValidationMessageCodes.*; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; + +import org.springframework.validation.annotation.Validated; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Builder +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PUBLIC) +@ToString +@ForwardingPasswordSizeConstraint +@Validated +public class RedirectRequest { + + @NotEmpty(message = FIELD_IS_EMPTY) + @Email(message = FIELD_INVALID) + private String email; + + @JsonProperty(access = Access.WRITE_ONLY) + private char[] password; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java new file mode 100644 index 0000000000000000000000000000000000000000..e9ca6501ad8849614300964ff7e790dfff82beb6 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/Wiedervorlage.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static de.itvsh.goofy.common.ValidationMessageCodes.*; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.List; + +import javax.validation.constraints.FutureOrPresent; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +import de.itvsh.goofy.common.LinkedResource; +import de.itvsh.goofy.common.LinkedUserProfileResource; +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.command.CommandBody; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.ToString; + +@Getter +@Builder(toBuilder = true) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@ToString +public class Wiedervorlage implements CommandBody { + + @JsonIgnore + private String id; + + @JsonIgnore + private String vorgangId; + @JsonIgnore + private long version; + + @JsonProperty(access = Access.READ_ONLY) + private boolean done; + + @JsonIgnore + @LinkedUserProfileResource + private String createdBy; + @JsonProperty(access = Access.READ_ONLY) + private ZonedDateTime createdAt; + + @NotNull(message = FIELD_IS_EMPTY) + @Size(min = 3, max = 40, message = FIELD_SIZE) + private String betreff; + private String beschreibung; + + @NotNull(message = FIELD_IS_EMPTY) + @FutureOrPresent(message = FIELD_DATE_PAST) + private LocalDate frist; + + @LinkedResource(controllerClass = BinaryFileController.class) + @Singular + private List<FileId> attachments; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommand.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..1ebea270fefe937aca49bf4e94257b6e802a285d --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommand.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import javax.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandStatus; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@EqualsAndHashCode +class WiedervorlageCommand { + + @JsonIgnore + private String id; + + private CommandOrder order; + + @JsonIgnore + private CommandStatus status; + + @Valid + private Wiedervorlage wiedervorlage; +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandController.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandController.java new file mode 100644 index 0000000000000000000000000000000000000000..2709e4c45f359473678f61769f879b5b5c3e251b --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandController.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.kop.common.errorhandling.TechnicalException; + +@RestController +@RequestMapping(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS) +public class WiedervorlageCommandController { + + static final String WIEDERVORLAGE_COMMANDS = "/api/wiedervorlages/{wiedervorlageId}/{wiedervorlageVersion}/commands"; + + @Autowired + private WiedervorlageService service; + + @PostMapping + public ResponseEntity<Void> updateWiedervorlage(@RequestBody WiedervorlageCommand command, @PathVariable String wiedervorlageId, + @PathVariable long wiedervorlageVersion) { + var wiedervorlage = service.getById(wiedervorlageId); + var createdCommand = createCommand(wiedervorlage, command); + + service.updateNextFrist(wiedervorlage.getVorgangId()); + + return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build(); + } + + Command createCommand(Wiedervorlage wiedervorlage, WiedervorlageCommand command) { + switch (command.getOrder()) { + case WIEDERVORLAGE_ERLEDIGEN: { + return service.erledigen(wiedervorlage); + } + case WIEDERVORLAGE_WIEDEREROEFFNEN: { + return service.wiedereroeffnen(wiedervorlage); + } + case EDIT_WIEDERVORLAGE: { + return service.editWiedervorlage(updateWiedervorlageByCommand(wiedervorlage, command), wiedervorlage.getId(), wiedervorlage.getVersion()); + } + default: + throw new TechnicalException("Unsupported order " + command.getOrder()); + } + } + + Wiedervorlage updateWiedervorlageByCommand(Wiedervorlage wiedervorlage, WiedervorlageCommand command) { + return wiedervorlage.toBuilder() + .clearAttachments().attachments(command.getWiedervorlage().getAttachments()) + .betreff(command.getWiedervorlage().getBetreff()) + .beschreibung(command.getWiedervorlage().getBeschreibung()) + .frist(command.getWiedervorlage().getFrist()).build(); + } + + @RestController + @RequestMapping(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG) + public static class WiedervorlageCommandByVorgangController { + + static final String WIEDERVORLAGE_COMMANDS_BY_VORGANG = "/api/vorgangs/{vorgangId}/wiedervorlageCommands"; + + @Autowired + private WiedervorlageService service; + + @PostMapping + public ResponseEntity<Void> createWiedervorlage(@RequestBody WiedervorlageCommand command, @PathVariable String vorgangId) { + var createdCommand = service.createWiedervorlage(command.getWiedervorlage(), vorgangId); + + service.updateNextFrist(vorgangId); + + return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageController.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageController.java new file mode 100644 index 0000000000000000000000000000000000000000..83a093727433ec17e6a062c7129be60ba2f14677 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageController.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.user.CurrentUserService; + +@RestController +@RequestMapping(WiedervorlageController.WIEDERVORLAGE_PATH) +public class WiedervorlageController { + + static final String WIEDERVORLAGE_PATH = "/api/wiedervorlages"; // NOSONAR + static final String PARAM_VORGANG_ID = "vorgangId"; + + @Autowired + private WiedervorlageService service; + @Autowired + private WiedervorlageModelAssembler modelAssembler; + + @PreAuthorize(CurrentUserService.VERIFY_HAS_ROLE_VERWALTUNG_USER) + @GetMapping("/{wiedervorlageId}") + public EntityModel<Wiedervorlage> getById(@PathVariable String wiedervorlageId) { + return modelAssembler.toModel(service.getById(wiedervorlageId)); + } + + @GetMapping(params = PARAM_VORGANG_ID) + public CollectionModel<EntityModel<Wiedervorlage>> findByVorgang(@RequestParam String vorgangId) { + return modelAssembler.toCollectionModel(service.findByVorgangId(vorgangId), vorgangId); + } + + @RestController + @RequestMapping(AttachmentsByWiedervorlageController.PATH) + static class AttachmentsByWiedervorlageController { + + static final String PATH = "/api/wiedervorlages/{wiedervorlageId}"; // NOSONAR + + @Autowired + private BinaryFileController binaryFileController; + @Autowired + private WiedervorlageService service; + + @GetMapping("/attachments") + public CollectionModel<EntityModel<OzgFile>> getAttachments(@PathVariable String wiedervorlageId) { + var wiedervorlage = service.getById(wiedervorlageId); + + return binaryFileController.getFiles(wiedervorlage.getAttachments()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapper.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6d708991d5d5fff71928c4576525b2edc6e802f2 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapper.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; +import org.springframework.beans.factory.annotation.Autowired; + +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.binaryfile.FileIdMapper; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItem; + +@Mapper(uses = { FileIdMapper.class }, unmappedTargetPolicy = ReportingPolicy.WARN, // + unmappedSourcePolicy = ReportingPolicy.WARN, // + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, // + collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED) +abstract class WiedervorlageMapper { + + @Autowired + private GrpcObjectMapper grpcObjectMapper; + + static final String ID = "id"; + static final String DONE = "done"; + static final String CREATED_BY = "createdBy"; + static final String CREATED_BY_NAME = "createdByName"; + static final String CREATED_AT = "createdAt"; + static final String BETREFF = "betreff"; + static final String BESCHREIBUNG = "beschreibung"; + static final String FRIST = "frist"; + static final String ATTACHMENTS = "attachments"; + + public Wiedervorlage fromItem(GrpcVorgangAttachedItem item) { + return fromItemMap(grpcObjectMapper.mapFromGrpc(item.getItem()), item.getVersion()); + } + + Wiedervorlage fromItemMap(Map<String, Object> itemMap, long version) { + return Wiedervorlage.builder() + .id((String) itemMap.get(ID)) + .version(version) + .done(Boolean.valueOf((String) itemMap.get(DONE))) + .createdBy((String) itemMap.get(CREATED_BY)) + .createdAt(ZonedDateTime.parse((String) itemMap.get(CREATED_AT))) + .betreff((String) itemMap.get(BETREFF)) + .beschreibung((String) itemMap.get(BESCHREIBUNG)) + .frist(LocalDate.parse((String) itemMap.get(FRIST))) + .attachments(mapAttachments(itemMap.get(ATTACHMENTS))) + .build(); + } + + private List<FileId> mapAttachments(Object attachments) { + if (Objects.isNull(attachments)) { + return Collections.emptyList(); + } + if (attachments instanceof List<?> attachmentList) { + return attachmentList.stream().map(String::valueOf).map(FileId::from).toList(); + } + return Collections.singletonList(FileId.from((String) attachments)); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssembler.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..42a15247eca1283cb58bf2e8b8dbe467ab044f0a --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssembler.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.common.ModelBuilder; +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController; +import de.itvsh.goofy.wiedervorlage.WiedervorlageController.AttachmentsByWiedervorlageController; + +@Component +class WiedervorlageModelAssembler implements RepresentationModelAssembler<Wiedervorlage, EntityModel<Wiedervorlage>> { + + static final String REL_EDIT = "edit"; + static final String REL_ERLEDIGEN = "erledigen"; + static final String REL_WIEDEREROEFFNEN = "wiedereroeffnen"; + static final String REL_ATTACHMENTS = "attachments"; + + static final String REL_CREATE = "create-wiedervorlage"; + static final String REL_UPLOAD_FILE = "uploadFile"; + + private static final Predicate<Wiedervorlage> ALLOW_ERLEDIGEN = wiedervorlage -> !wiedervorlage.isDone(); + private static final Predicate<Wiedervorlage> ALLOW_WIEDEREROEFFNEN = Wiedervorlage::isDone; + private static final Predicate<Wiedervorlage> HAS_ATTACHMENTS = wiedervorlage -> !wiedervorlage.getAttachments().isEmpty(); + + @Override + public EntityModel<Wiedervorlage> toModel(Wiedervorlage wiedervorlage) { + var selfLink = linkTo(WiedervorlageController.class).slash(wiedervorlage.getId()); + var commandLink = linkTo( + methodOn(WiedervorlageCommandController.class).updateWiedervorlage(null, wiedervorlage.getId(), wiedervorlage.getVersion())); + + return ModelBuilder.fromEntity(wiedervorlage).addLink(selfLink.withSelfRel()) + .addLink(commandLink.withRel(REL_EDIT)) + .ifMatch(ALLOW_ERLEDIGEN).addLink(commandLink.withRel(REL_ERLEDIGEN)) + .ifMatch(ALLOW_WIEDEREROEFFNEN).addLink(commandLink.withRel(REL_WIEDEREROEFFNEN)) + .ifMatch(HAS_ATTACHMENTS) + .addLink(linkTo(methodOn(AttachmentsByWiedervorlageController.class).getAttachments(wiedervorlage.getId())).withRel(REL_ATTACHMENTS)) + .buildModel(); + } + + public CollectionModel<EntityModel<Wiedervorlage>> toCollectionModel(Stream<Wiedervorlage> entities, String vorgangId) { + return CollectionModel.of(entities.map(this::toModel).collect(Collectors.toList()), + linkTo(WiedervorlageController.class).withSelfRel(), + linkTo(methodOn(WiedervorlageCommandByVorgangController.class).createWiedervorlage(null, vorgangId)) + .withRel(REL_CREATE), + linkTo(BinaryFileController.class).slash(vorgangId).slash("wiedervorlageAttachment").slash("file") + .withRel(REL_UPLOAD_FILE)); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteService.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..09cf218b8b31763d0c7d7f2d1246dd2c1da4961f --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteService.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.stream.Stream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import de.itvsh.goofy.GoofyServerApplication; +import de.itvsh.ozg.pluto.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcAccessPermission; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcDeleteClientAttributeRequest; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcSetClientAttributeRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +public class WiedervorlageRemoteService { + + static final String CLIENT_ATTRIBUTE_NEXT_WIEDERVORLAGE_FRIST = "nextWiedervorlageFrist"; + static final String ITEM_NAME = "Wiedervorlage"; + + @GrpcClient(GoofyServerApplication.GRPC_CLIENT) + private VorgangAttachedItemServiceBlockingStub vorgangAttachedItemServiceStub; + + @GrpcClient(GoofyServerApplication.GRPC_CLIENT) + private ClientAttributeServiceBlockingStub clientAttributeServiceStub; + + @Autowired + private WiedervorlageMapper mapper; + @Autowired + private ApplicationContext context; + + public Stream<Wiedervorlage> findByVorgangId(String vorgangId) { + var response = vorgangAttachedItemServiceStub.find(buildFindRequest(vorgangId)); + + return response.getVorgangAttachedItemsList().stream().map(mapper::fromItem); + } + + GrpcFindVorgangAttachedItemRequest buildFindRequest(String vorgangId) { + return GrpcFindVorgangAttachedItemRequest.newBuilder() + .setVorgangId(vorgangId) + .setItemName(ITEM_NAME) + .build(); + } + + public Wiedervorlage getById(String wiedervorlageId) { + var response = vorgangAttachedItemServiceStub.getById(GrpcVorgangAttachedItemRequest.newBuilder().setId(wiedervorlageId).build()); + + return mapper.fromItem(response.getVorgangAttachedItem()).toBuilder().vorgangId(response.getVorgangAttachedItem().getVorgangId()).build(); + } + + public void updateNextFrist(String vorgangId, Optional<LocalDate> nextFrist) { + nextFrist.ifPresentOrElse(frist -> setNextFrist(vorgangId, frist), () -> deleteNextFrist(vorgangId)); + } + + void deleteNextFrist(String vorgangId) { + clientAttributeServiceStub.delete(GrpcDeleteClientAttributeRequest.newBuilder() + .setVorgangId(vorgangId) + .setClientName(context.getId()) + .setAttributeName(CLIENT_ATTRIBUTE_NEXT_WIEDERVORLAGE_FRIST) + .build()); + } + + void setNextFrist(String vorgangId, LocalDate nextFrist) { + clientAttributeServiceStub.set(buildUpdateNextFristRequest(vorgangId, nextFrist)); + } + + private GrpcSetClientAttributeRequest buildUpdateNextFristRequest(String vorgangId, LocalDate nextFrist) { + return GrpcSetClientAttributeRequest.newBuilder() + .setVorgangId(vorgangId) + .setAttribute(GrpcClientAttribute.newBuilder() + .setAccess(GrpcAccessPermission.READ_ONLY) + .setAttributeName(CLIENT_ATTRIBUTE_NEXT_WIEDERVORLAGE_FRIST) + .setValue(GrpcClientAttributeValue.newBuilder().setStringValue(nextFrist.format(DateTimeFormatter.ISO_DATE))) + .build()) + .build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageService.java b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageService.java new file mode 100644 index 0000000000000000000000000000000000000000..e30ff0903ad141582f34128bdfd1dd01b01d8894 --- /dev/null +++ b/goofy-server/src/main/java/de/itvsh/goofy/wiedervorlage/WiedervorlageService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import de.itvsh.goofy.common.attacheditem.VorgangAttachedItemService; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.user.CurrentUserService; + +@Validated +@Service +class WiedervorlageService { + + private static final Predicate<Wiedervorlage> IS_NOT_DONE = wiedervorlage -> !wiedervorlage.isDone(); + + @Autowired + private WiedervorlageRemoteService remoteService; + @Autowired + private VorgangAttachedItemService vorgangAttachedItemService; + @Autowired + private CurrentUserService currentUserService; + + public Command createWiedervorlage(@Valid Wiedervorlage wiedervorlage, String vorgangId) { + return vorgangAttachedItemService.createNewWiedervorlage(addCreated(wiedervorlage), vorgangId); + } + + Wiedervorlage addCreated(Wiedervorlage wiedervorlage) { + return wiedervorlage.toBuilder() + .createdAt(ZonedDateTime.now().withNano(0)) + .createdBy(currentUserService.getUserId().toString()) + .build(); + } + + public Command editWiedervorlage(@Valid Wiedervorlage wiedervorlage, String wiedervorlageId, long version) { + return vorgangAttachedItemService.editWiedervorlage(wiedervorlage, wiedervorlageId, version); + } + + public Wiedervorlage getById(String wiedervorlageId) { + return remoteService.getById(wiedervorlageId); + } + + @Async + public void updateNextFrist(String vorgangId) { + var allWiedervorlagen = findByVorgangId(vorgangId); + + remoteService.updateNextFrist(vorgangId, calculateNextFrist(allWiedervorlagen)); + } + + Optional<LocalDate> calculateNextFrist(Stream<Wiedervorlage> wiedervorlagen) { + return wiedervorlagen + .filter(Objects::nonNull) + .filter(IS_NOT_DONE) + .sorted(Comparator.comparing(Wiedervorlage::getFrist)) + .map(Wiedervorlage::getFrist) + .findFirst(); + } + + public Stream<Wiedervorlage> findByVorgangId(String vorgangId) { + return remoteService.findByVorgangId(vorgangId); + } + + Command erledigen(Wiedervorlage wiedervorlage) { + return vorgangAttachedItemService.setWiedervorlageDone(wiedervorlage, true); + } + + Command wiedereroeffnen(Wiedervorlage wiedervorlage) { + return vorgangAttachedItemService.setWiedervorlageDone(wiedervorlage, false); + } + +} \ No newline at end of file diff --git a/goofy-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/goofy-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..8b6ef837da5a5d749a02ec15755c3edb2aabf436 --- /dev/null +++ b/goofy-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,9 @@ +{ + "properties": [ + { + "name": "goofy.production", + "type": "java.lang.Boolean", + "description": "Enables/Disables the production mode. (default: true)" + } + ] +} \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-dev.yml b/goofy-server/src/main/resources/application-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..2e7f1d297f8df38f021e38fc122988f2fb8745f5 --- /dev/null +++ b/goofy-server/src/main/resources/application-dev.yml @@ -0,0 +1,11 @@ +goofy: + production: false + +keycloak: + auth-server-url: https://sso.dev.ozg-sh.de + realm: sh-kiel-dev + resource: sh-kiel-dev-goofy + +server: + error: + include-stacktrace: always \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-e2e.yml b/goofy-server/src/main/resources/application-e2e.yml new file mode 100644 index 0000000000000000000000000000000000000000..ae50cc668ec4497e586270ac708e3a23dbd60720 --- /dev/null +++ b/goofy-server/src/main/resources/application-e2e.yml @@ -0,0 +1,11 @@ +keycloak: + realm: sh-e2e-dev + resource: sh-e2e-dev-goofy + +kop: + forwarding: + lninfo: + url: classpath:files/LandesnetzInfo.html + user-manager: + url: http://localhost:9092 + internalurl: http://ozg-usermanager:8080 \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-ea.yml b/goofy-server/src/main/resources/application-ea.yml new file mode 100644 index 0000000000000000000000000000000000000000..58740a745daf75b078572400c0d3ce0b1b6e6104 --- /dev/null +++ b/goofy-server/src/main/resources/application-ea.yml @@ -0,0 +1,28 @@ +kop: + forwarding: + lninfo: + url: http://lninfo.ozg-tools.svc.cluster.local/ + +--- +spring: + config: + activate: + on-profile: + - dev + +kop: + forwarding: + lninfo: + url: classpath:files/LandesnetzInfo.html + +--- +spring: + config: + activate: + on-profile: + - test + +kop: + forwarding: + lninfo: + url: classpath:files/LandesnetzInfo.html \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-local.yml b/goofy-server/src/main/resources/application-local.yml new file mode 100644 index 0000000000000000000000000000000000000000..336c6f9170989e553e7e5d33999774709f502a03 --- /dev/null +++ b/goofy-server/src/main/resources/application-local.yml @@ -0,0 +1,23 @@ +logging: + level: + '[de.itvsh]': INFO + config: classpath:log4j2-local.xml + +goofy: + production: false + +keycloak: + auth-server-url: http://localhost:8088 + realm: sh-kiel-dev + resource: sh-kiel-dev-goofy + +server: + error: + include-stacktrace: always + +kop: + user-manager: + url: http://localhost:9092 + internalurl: http://localhost:9092 + profile-template: /api/userProfiles/%s + search-template: /api/userProfiles/?searchBy={searchBy} \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-oc.yml b/goofy-server/src/main/resources/application-oc.yml new file mode 100644 index 0000000000000000000000000000000000000000..2f098ef2b675bca901ca675c70d5dc71915fff86 --- /dev/null +++ b/goofy-server/src/main/resources/application-oc.yml @@ -0,0 +1,14 @@ +spring: + boot: + admin: + client: + instance: + prefer-ip: true + +grpc: + client: + pluto: + address: static://172.30.125.177:9090 + +logging: + config: classpath:log4j2.xml \ No newline at end of file diff --git a/goofy-server/src/main/resources/application-remotekc.yml b/goofy-server/src/main/resources/application-remotekc.yml new file mode 100644 index 0000000000000000000000000000000000000000..a9c24daa3d8cf653c70c5e9700fae348d1cc5882 --- /dev/null +++ b/goofy-server/src/main/resources/application-remotekc.yml @@ -0,0 +1,6 @@ +keycloak: + realm: sh-kiel-dev + resource: sh-kiel-dev-goofy + public-client: true + use-resource-role-mappings: true + auth-server-url: https://sso.dev.ozg-sh.de diff --git a/goofy-server/src/main/resources/application.yml b/goofy-server/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..4d9c4b01b56ad1f9b4a16693cb9cecd7d107a2b6 --- /dev/null +++ b/goofy-server/src/main/resources/application.yml @@ -0,0 +1,77 @@ +logging: + level: + ROOT: WARN + '[de.itvsh]': INFO, + '[org.springframework.security]': WARN + '[org.keycloak.adapters]': WARN + +spring: + mvc: + pathmatch: + matching-strategy: ant-path-matcher + application: + name: Goofy + jackson: + deserialization: + 'ADJUST_DATES_TO_CONTEXT_TIME_ZONE': false + servlet: + multipart: + max-file-size: 2GB + max-request-size: 2GB + +server: + http2: + enabled: true + error: + include-stacktrace: never + +management: + server: + port: 8081 + health: + livenessState: + enabled: true + readinessState: + enabled: true + endpoint: + health: + group: + exploratory: + include: livenessState,readinessState,ping + show-details: always + probes: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: health,prometheus + +goofy: + production: true + +keycloak: + auth-server-url: http://localhost:8088 + realm: sh-kiel-dev + resource: sh-kiel-dev-goofy + public-client: true + use-resource-role-mappings: true + +grpc: + client: + pluto: + address: static://127.0.0.1:9090 + negotiationType: PLAINTEXT + +kop: + auth: + token: + validity: 60000 + upload: + maxFileSize: + postfachNachrichtAttachment: 3MB + wiedervorlageAttachment: 40MB + user-manager: + profile-template: /api/userProfiles/%s + search-template: /api/userProfiles/?searchBy={searchBy} \ No newline at end of file diff --git a/goofy-server/src/main/resources/banner.txt b/goofy-server/src/main/resources/banner.txt new file mode 100644 index 0000000000000000000000000000000000000000..13e3273b90490767f580107cfd3fec70dcb2ba0d --- /dev/null +++ b/goofy-server/src/main/resources/banner.txt @@ -0,0 +1,6 @@ + ___ ___ ___ ___ __ __ + / __| / _ \ / _ \ | __|\ \ / / +| (_ || (_) || (_) || _| \ / + \___| \___/ \___/ |_| |_| +================================ +${spring-boot.version} ${application.version} diff --git a/goofy-server/src/main/resources/fop/postfach-nachrichten.xsl b/goofy-server/src/main/resources/fop/postfach-nachrichten.xsl new file mode 100644 index 0000000000000000000000000000000000000000..b030aacd5e9d4f8127ce23fe7a65b7cf5402da04 --- /dev/null +++ b/goofy-server/src/main/resources/fop/postfach-nachrichten.xsl @@ -0,0 +1,140 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.1"> + + <xsl:template name="functional-template" match="/testModel"> + <fo:flow flow-name="xsl-region-body"> + + <fo:block font-size="14pt"> + <fo:table> + <fo:table-column column-width="50mm" /> + <fo:table-column column-width="125mm"/> + <fo:table-body> + <fo:table-row> + <fo:table-cell> + <fo:block>Vorgangsname</fo:block> + </fo:table-cell> + <fo:table-cell> + <fo:block><xsl:value-of select="postfachNachrichtPdfModel/vorgangName" /></fo:block> + </fo:table-cell> + </fo:table-row> + + <fo:table-row> + <fo:table-cell> + <fo:block>Vorgangsnummer</fo:block> + </fo:table-cell> + <fo:table-cell> + <fo:block><xsl:value-of select="postfachNachrichtPdfModel/vorgangNummer" /></fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + </fo:block> + + <fo:block font-size="11pt" margin-top="30px"> + <fo:table> + <fo:table-column column-width="50mm" /> + <fo:table-column column-width="125mm"/> + <fo:table-body> + <fo:table-row> + <fo:table-cell> + <fo:block>Antragsteller</fo:block> + </fo:table-cell> + <fo:table-cell> + <fo:block> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerAnrede" /> + <xsl:text> </xsl:text> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerVorname" /> + <xsl:text> </xsl:text> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerNachname" /> + </fo:block> + <fo:block> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerStrasse" /> + <xsl:text> </xsl:text> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerHausnummer" /> + </fo:block> + <fo:block> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerPlz" /> + <xsl:text> </xsl:text> + <xsl:value-of select="postfachNachrichtPdfModel/antragstellerOrt" /> + </fo:block> + </fo:table-cell> + </fo:table-row> + + </fo:table-body> + </fo:table> + </fo:block> + + <fo:block-container font-size="11pt" margin-top="1cm"> + <fo:block font-size="14pt" margin-bottom="3mm">Nachrichten</fo:block> + + <xsl:for-each select="postfachNachrichtPdfModel/nachrichten/nachricht"> + <xsl:call-template name="nachricht"/> + </xsl:for-each> + </fo:block-container> + </fo:flow> + </xsl:template> + + <xsl:template name="nachricht"> + + <fo:block font-size="11pt" margin-bottom="2mm"> + <xsl:if test="isFirst='false'"> + <fo:leader leader-pattern="rule" leader-length="175mm" rule-style="solid" rule-thickness="1pt"/> + </xsl:if> + <fo:table> + <fo:table-column column-width="30mm" /> + <fo:table-column column-width="145mm"/> + <fo:table-body> + <fo:table-row> + <fo:table-cell padding="3px"> + <fo:block>Absender</fo:block> + </fo:table-cell> + <fo:table-cell padding="3px"> + <fo:block><xsl:value-of select="createdBy" /></fo:block> + </fo:table-cell> + </fo:table-row> + + <fo:table-row> + <fo:table-cell padding="3px"> + <fo:block>Datum</fo:block> + </fo:table-cell> + <fo:table-cell padding="3px"> + <fo:block><xsl:value-of select="createdAt" /></fo:block> + </fo:table-cell> + </fo:table-row> + + <fo:table-row> + <fo:table-cell padding="3px"> + <fo:block>Betreff</fo:block> + </fo:table-cell> + <fo:table-cell padding="3px"> + <fo:block><xsl:value-of select="subject" /></fo:block> + </fo:table-cell> + </fo:table-row> + + <fo:table-row> + <fo:table-cell padding="3px"> + <fo:block>Text</fo:block> + </fo:table-cell> + <fo:table-cell padding="3px"> + <fo:block><xsl:value-of select="mailBody" /></fo:block> + </fo:table-cell> + </fo:table-row> + + <fo:table-row> + <fo:table-cell padding="3px"> + <fo:block>Anhänge</fo:block> + </fo:table-cell> + <xsl:if test="attachments/attachment"> + <fo:table-cell padding="3px"> + <xsl:for-each select="attachments/attachment"> + <fo:block><xsl:value-of select="."/></fo:block> + </xsl:for-each> + </fo:table-cell> + </xsl:if> + </fo:table-row> + </fo:table-body> + </fo:table> + </fo:block> + + </xsl:template> + +</xsl:stylesheet> \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/ApplicationTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/ApplicationTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..cc1f9697fc00765c1dd19f38c481e9da99b4f6f9 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/ApplicationTestFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import com.thedeanda.lorem.LoremIpsum; + +public class ApplicationTestFactory { + + public static final String CONTEXT_ID = LoremIpsum.getInstance().getWords(1); +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/EnvironmentControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/EnvironmentControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1cf9aefddc08317f7b66661ad848caad645e2652 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/EnvironmentControllerTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +class EnvironmentControllerTest { + + private final String PATH = "/api/environment"; + + @InjectMocks + private EnvironmentController controller; + @Mock + private KeycloakSpringBootProperties kcProperties; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + void loadEnvironment() throws Exception { + mockMvc.perform(get(PATH)).andExpect(status().isOk()); + } + + @Test + void shouldHaveProductionTrueAsDefault() throws Exception { + mockMvc.perform(get(PATH)).andExpect(status().is2xxSuccessful())// + .andExpect(jsonPath("$.production").value(true)); + } + + @Test + void shouldHaveClientId() throws Exception { + var client = "goofy"; + + when(kcProperties.getResource()).thenReturn(client); + + mockMvc.perform(get(PATH)).andExpect(jsonPath("$.clientId").value(client)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/GoofyServerApplicationTest.java b/goofy-server/src/test/java/de/itvsh/goofy/GoofyServerApplicationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..83a13fde60745e5f8be87535b78a946acf097d92 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/GoofyServerApplicationTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class GoofyServerApplicationTest { + + @Test + void contextLoads() { // NOSONAR + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/JwtTokenUtilTest.java b/goofy-server/src/test/java/de/itvsh/goofy/JwtTokenUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..92a75d932300994bec622d8b8c23c0a774b6d0cd --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/JwtTokenUtilTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.auth0.jwt.exceptions.JWTVerificationException; + +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.downloadtoken.DownloadTokenProperties; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.ZustaendigeStelleTestFactory; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +class JwtTokenUtilTest { + + @Spy + @InjectMocks + private JwtTokenUtil jwtTokenUtil; + @Mock + private DownloadTokenProperties downloadTokenProperties; + + private static final String TOKEN_SECRET = "t0pS3cr3t"; + private static final int TOKEN_VALIDITY = 60000; + + @BeforeEach + public void initTest() throws Exception { + when(downloadTokenProperties.getSecret()).thenReturn(TOKEN_SECRET); + } + + @Nested + @DisplayName("Verify token generation") + class TestGenerateToken { + + private String generatedToken; + + @BeforeEach + void initTest() { + when(downloadTokenProperties.getSecret()).thenReturn(TOKEN_SECRET); + when(downloadTokenProperties.getValidity()).thenReturn(TOKEN_VALIDITY); + + generatedToken = jwtTokenUtil.generateToken(FileId.createNew(), UserProfileTestFactory.create()); + } + + @Test + void userId() { + var userId = getParsedBody().getSubject(); + + assertThat(userId).isEqualTo(SecurityTestFactory.SUBJECT.toString()); + } + + @Test + void expirationDate() { + var before = new Date(); + var expirationDate = getParsedBody().getExpiration(); + var after = new Date(System.currentTimeMillis() + 900000); + + assertThat(expirationDate).isAfter(before).isBefore(after); + } + + @Test + void organisationseinheitIds() { + var organisationseinheitIds = jwtTokenUtil.getOrganisationseinheitIdsFromToken(generatedToken); + + assertThat(organisationseinheitIds).isEqualTo(List.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID)); + } + + private Claims getParsedBody() { + return Jwts.parser().setSigningKey(TOKEN_SECRET.getBytes()).parseClaimsJws(generatedToken).getBody(); + } + } + + @Nested + class TestVerifyToken { + + @Test + void shouldDoNotThrowExcepionOnValidToken() { + var token = buildToken(UUID.randomUUID().toString(), TOKEN_SECRET, TOKEN_VALIDITY); + + jwtTokenUtil.verifyToken(token); + } + + @Test + void shouldThrowExceptionOnInvalidToken() { + var token = buildToken(UUID.randomUUID().toString(), "invalid_token", TOKEN_VALIDITY); + + assertThrows(JWTVerificationException.class, () -> jwtTokenUtil.verifyToken(token)); + } + + @Test + void shouldThrowExceptionOnTimeExpired() { + var token = buildToken(UUID.randomUUID().toString(), TOKEN_SECRET, -1000); + + assertThrows(JWTVerificationException.class, () -> jwtTokenUtil.verifyToken(token)); + } + + private String buildToken(String subject, String token, int expiredTime) { + Map<String, Object> claims = new HashMap<>(); + claims.put(JwtTokenUtil.FIRSTNAME_CLAIM, SecurityTestFactory.USER_FIRSTNAME); + claims.put(JwtTokenUtil.LASTNAME_CLAIM, SecurityTestFactory.USER_LASTNAME); + claims.put(JwtTokenUtil.ROLE_CLAIM, SecurityTestFactory.AUTHORITIES); + claims.put(JwtTokenUtil.FILEID_CLAIM, FileId.createNew()); + claims.put(JwtTokenUtil.ORGANSIATIONSEINHEIT_IDS_CLAIM, SecurityTestFactory.ORGANSIATIONSEINHEIT_IDS); + + return Jwts.builder() + .setClaims(claims) + .setSubject(subject) + .setHeaderParam(JwtTokenUtil.TOKEN_TYP_KEY, JwtTokenUtil.TOKEN_TYPE) + .setIssuer(JwtTokenUtil.TOKEN_ISSUER).setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + expiredTime)) + .setAudience(JwtTokenUtil.TOKEN_AUDIENCE) + .signWith(SignatureAlgorithm.HS512, token.getBytes()) + .compact(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/RequestAttributesTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/RequestAttributesTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..8109e2ead0ddf83e4ae767c48e54fed305319b2d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/RequestAttributesTestFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.UUID; + +public class RequestAttributesTestFactory { + + public static final String REQUEST_ID = UUID.randomUUID().toString(); + + public static RequestAttributes create() { + return createBuilder().build(); + } + + public static RequestAttributes.RequestAttributesBuilder createBuilder() { + return RequestAttributes.builder() + .requestId(REQUEST_ID); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..66b7a4de750a5bb61aa9e8154712f39432edc493 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterITCase.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +@WithMockUser +class RequestIdFilterITCase { + + @SpyBean + private RequestIdFilter filter; + + @Autowired + private MockMvc mockMvc; + + @Test + void shouldCallFilterMethod() throws Exception { + mockMvc.perform(get("/")); + + verify(filter).doFilterInternal(any(), any(), any()); + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterTest.java b/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0c3e90c1641cbc6489f0ba36ee35ac37c3328fdc --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/RequestIdFilterTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +class RequestIdFilterTest { + + @InjectMocks + private RequestIdFilter filter; + @Mock + private FilterChain filterChain; + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private CallScope callScope; + + @Spy + private RequestAttributes attributes; + + @Test + void shouldContinueFilterChain() throws IOException, ServletException { + callDoFilterInternal(); + + verify(filterChain).doFilter(request, response); + } + + @Test + void shouldStartCallScope() { + callDoFilterInternal(); + + verify(callScope).startScope(); + } + + @Test + void shouldEndCallScopeAfterException() throws IOException, ServletException { + doThrow(RuntimeException.class).when(filterChain).doFilter(any(), any()); + + assertThrows(RuntimeException.class, this::callDoFilterInternal); + + verify(callScope).endScope(); + } + + private void callDoFilterInternal() { + try { + filter.doFilterInternal(request, response, filterChain); + } catch (ServletException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..079520b7a8c70287892fbd67f7ce88fc029f8a79 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/RootControllerTest.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.boot.info.BuildProperties; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserRemoteService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.system.SystemStatusService; + +class RootControllerTest { + + @Spy + @InjectMocks // NOSONAR + private RootController controller; + @Mock + private BuildProperties properties; + @Mock + private CurrentUserService currentUserService; + @Mock + private SystemStatusService systemStatusService; + @Mock + private UserRemoteService internalUserIdService; + @Mock + private UserManagerUrlProvider userManagerUrlProvider; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + + when(currentUserService.getUserId()).thenReturn(UserId.from("42")); + when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty()); + when(userManagerUrlProvider.getUserProfileSearchTemplate()).thenReturn("UserProfileSearchTemplateDummy/$"); + } + + @DisplayName("Links for user") + @Nested + class TestLinks { + + @DisplayName("with role " + UserRole.VERWALTUNG_USER) + @Nested + class TestWithVerwaltungRole { + + @BeforeEach + void mockCurrentUserService() { + doReturn(true).when(controller).hasVerwaltungRole(); + when(systemStatusService.isSearchServerAvailable()).thenReturn(true); + } + + @Test + void shouldHaveSelfLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent(); + } + + @Test + void shouldHaveLinkToVorgangList() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_VORGAENGE)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/vorgangs"); + } + + @Test + void shouldHaveSearchLinkWithLimit() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/vorgangs?page=0{&searchBy,limit,assignedTo}"); + } + + @Test + void shouldHaveMyVorgaengeLink() { + when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID)); + + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_MY_VORGAENGE)).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/vorgangs?page=0&assignedTo=" + UserProfileTestFactory.ID.toString() + "{&searchBy,limit}"); + } + + @Test + void shouldHaveSearchMyVorgaengeLink() { + when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID)); + + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH_MY_VORGAENGE)).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/vorgangs?page=0&assignedTo=" + UserProfileTestFactory.ID.toString() + "{&searchBy,limit}"); + } + + @Test + void shouldHaveDownloadTokenLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_DOWNLOAD_TOKEN)).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/downloadtoken"); + } + + @DisplayName("search service not available") + @Nested + class TestWithoutSearchService { + + @BeforeEach + void init() { + when(systemStatusService.isSearchServerAvailable()).thenReturn(false); + } + + @Test + void shouldNotHaveSearchLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH)).isNotPresent(); + } + + @Test + void shouldHaveMyVorgaengeLink() { + when(internalUserIdService.getUserId(any())).thenReturn(Optional.of(UserProfileTestFactory.ID)); + + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_MY_VORGAENGE)).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/vorgangs?page=0&assignedTo=" + UserProfileTestFactory.ID.toString() + "{&searchBy,limit}"); + } + + @Test + void shouldNotHaveSearchMyVorgaengeLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH_MY_VORGAENGE)).isNotPresent(); + } + } + } + + @DisplayName("with role " + UserRole.EINHEITLICHER_ANSPRECHPARTNER) + @Nested + class TestWithEinheitlicherAnsprechpartnerRole { + + @BeforeEach + void mock() { + when(currentUserService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(true); + + doReturn(false).when(controller).hasVerwaltungRole(); + } + + @Test + void shoulNotHaveMyVorgaengeLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_MY_VORGAENGE)).isNotPresent(); + } + + @Test + void shoulNotHaveSearchMyVorgaengeLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH_MY_VORGAENGE)).isNotPresent(); + } + + @DisplayName("search service not available") + @Nested + class TestWithoutSearchService { + + @Test + void shouldNotHaveSearchLink() { + when(systemStatusService.isSearchServerAvailable()).thenReturn(false); + + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH)).isNotPresent(); + } + } + } + + @DisplayName("with no role") + @Nested + class TestWithNoRole { + + @BeforeEach + void mockCurrentUserService() { + when(currentUserService.hasRole(anyString())).thenReturn(false); + } + + @Test + void shouldHaveNoLinkAtAll() { + var model = controller.getRootResource(); + + assertThat(model.getLinks()).isEmpty(); + } + } + + @DisplayName("current user") + @Nested + class CurrentUser { + + @DisplayName("when usermanager url is configured") + @Nested + class UsermanagerUrlConfigured { + + private String userProfileTemplate = "UserProfileTemplate/%s"; + + @BeforeEach + void mock() { + when(currentUserService.getUserId()).thenReturn(UserProfileTestFactory.ID); + + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(true); + when(userManagerUrlProvider.getUserProfileTemplate()).thenReturn(userProfileTemplate); + } + + @Test + void shouldHaveCurrentUserLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_CURRENT_USER)).isPresent().get().extracting(Link::getHref) + .isEqualTo("UserProfileTemplate/" + UserProfileTestFactory.ID.toString()); + } + } + + @DisplayName("when usermanager url is not configured") + @Nested + class UsermanagerUrlNotConfigured { + + @BeforeEach + void mock() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + } + + @Test + void shouldNotHaveCurrentUserLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_CURRENT_USER)).isNotPresent(); + } + } + + @DisplayName("when external Id cannot resolved to internal id") + @Nested + class NoInternalUserId { + + @BeforeEach + void init() { + doReturn(true).when(controller).hasVerwaltungRole(); + + when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty()); + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + } + + @Test + void shouldNotHaveMyVorgaengeLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_MY_VORGAENGE)).isNotPresent(); + } + + @Test + void shouldNotHaveSearchMyVorgaengeLink() { + var model = controller.getRootResource(); + + assertThat(model.getLink(RootController.REL_SEARCH_MY_VORGAENGE)).isNotPresent(); + } + } + } + } + + @DisplayName("Root resource") + @Nested + class TestRootResource { + + @BeforeEach + void initTest() { + when(currentUserService.getUserId()).thenReturn(UserId.from("42")); + when(internalUserIdService.getUserId(any())).thenReturn(Optional.empty()); + when(userManagerUrlProvider.getUserProfileSearchTemplate()).thenReturn("UserProfileSearchTemplateDummy/$"); + } + + @Test + void shouldHaveJavaVersion() throws Exception { + callEndpoint().andExpect(jsonPath("$.javaVersion").exists()); + } + + @Test + void shouldHaveversion() throws Exception { + when(properties.getVersion()).thenReturn("42"); + + callEndpoint().andExpect(jsonPath("$.version").value("42")); + } + + @Test + void shouldHavebuildTime() throws Exception { + when(properties.getTime()).thenReturn(LocalDateTime.parse("2021-04-01T10:30").toInstant(ZoneOffset.UTC)); + + callEndpoint().andExpect(jsonPath("$.buildTime").exists()); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(RootController.PATH)).andExpect(status().isOk()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/SecurityTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/SecurityTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..dabbacc63d6161fd0e559a3071d9882d62e88069 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/SecurityTestFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class SecurityTestFactory { + + static final String SUBJECT = UserProfileTestFactory.ID.toString(); + static final String USER_FIRSTNAME = "Tim"; + static final String USER_LASTNAME = "Tester"; + static final String ROLE = "Testrolle"; + static final List<SimpleGrantedAuthority> AUTHORITIES = Arrays.asList(new SimpleGrantedAuthority(ROLE)); + static final Collection<String> ORGANSIATIONSEINHEIT_IDS = List.of("812546"); +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/TestSecurityConfiguration.java b/goofy-server/src/test/java/de/itvsh/goofy/TestSecurityConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..cc5791828256855e858c7485f445808f2b1c3fd4 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/TestSecurityConfiguration.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.WebSecurityConfigurer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@Order(50) +public class TestSecurityConfiguration extends WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { + + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + + http.csrf().disable(); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/attachment/AttachmentControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/attachment/AttachmentControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8c76728c9c5519e5888c5f34d8a50f0feb75e715 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/attachment/AttachmentControllerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.attachment; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileModelAssembler; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileService; +import de.itvsh.goofy.common.file.OzgFileTestFactory; + +class AttachmentControllerTest { + + private final String PATH = AttachmentController.ATTACHMENT_PATH; + + @InjectMocks + private AttachmentController controller; + @Mock + private BinaryFileModelAssembler modelAssembler; + @Mock + private OzgFileService fileService; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetAttachmentsByEingang { + + @BeforeEach + void mockFileService() { + when(fileService.getAttachmentsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); + } + + @Test + void shouldCallEndpoint() throws Exception { + callEndpoint(); + } + + @Test + void shoudlCallFileService() throws Exception { + callEndpoint(); + + verify(fileService).getAttachmentsByEingang(any()); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); + } + + private void callEndpoint() throws Exception { + mockMvc.perform(get(PATH).param(AttachmentController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/attachment/GrpcGetAttachmentsResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/attachment/GrpcGetAttachmentsResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..07ecb0d2c00ce0209ff6a70c20d07104f9a44564 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/attachment/GrpcGetAttachmentsResponseTestFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.attachment; + +import java.util.Collections; + +import de.itvsh.goofy.common.file.GrpcOzgFileTestFactory; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetAttachmentsResponse; +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; + +public class GrpcGetAttachmentsResponseTestFactory { + + private static GrpcOzgFile GRPC_FILE = GrpcOzgFileTestFactory.create(); + + public static GrpcGetAttachmentsResponse create() { + return createBuilder().build(); + } + + public static GrpcGetAttachmentsResponse.Builder createBuilder() { + return GrpcGetAttachmentsResponse.newBuilder() + .addAllFile(Collections.singleton(GRPC_FILE)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/GrpcCallContextTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/GrpcCallContextTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7422743e7270b5240c0e81b5bf529a59dc55e277 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/GrpcCallContextTestFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import de.itvsh.goofy.common.user.GrpcUserTestFactory; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.command.GrpcUser; + +public class GrpcCallContextTestFactory { + + private static final GrpcUser USER = GrpcUserTestFactory.create(); + public static final String CLIENT = "testGoofyClient"; + + public static GrpcCallContext create() { + return createBuilder().build(); + } + + public static GrpcCallContext.Builder createBuilder() { + return GrpcCallContext.newBuilder() + .setUser(USER) + .setClient(CLIENT); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/IdBuilderTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/IdBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..51d7374cf49a29660b9b7b0a1990cca1977fda29 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/IdBuilderTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.BeanProperty; + +class IdBuilderTest { + + @DisplayName("Test building ID when deserializing linked resources") + @Nested + class TestBuilingId { + private static final String ID = "id"; + + @Test + void shouldBuildId() { + IdBuilder idBuilder = new IdBuilder(); + + var idObject = idBuilder.build(ID); + + assertThat(idObject).isInstanceOf(Object.class).asString().isEqualTo(ID); + } + + @Test + void shouldCreateObjectBuilder() { + BeanProperty property = mock(BeanProperty.class); + ObjectBuilder<Object> idBuilder = new IdBuilder().constructContextAware(property); + + assertThat(idBuilder).isNotNull(); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceDeserializerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceDeserializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4fbc567d388ed16852209ab7d298d26a31482aa1 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceDeserializerTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.exc.StreamReadException; +import com.fasterxml.jackson.databind.DatabindException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class LinkedResourceDeserializerTest { + private static final String TEST_JSON = "{\"id\":\"/api/vorgangs/" + UserProfileTestFactory.ID.toString() + "\"}"; + + @DisplayName("Test the deserilization of linked resource json") + @Nested + class TestDeserialization { + @Test + void shouldDeserialize() throws StreamReadException, DatabindException, IOException { + LinkedResourceTestObject res = new ObjectMapper().readValue(TEST_JSON.getBytes(), LinkedResourceTestObject.class); + + assertThat(res).hasFieldOrPropertyWithValue("id", UserProfileTestFactory.ID); + } + + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceSerializerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceSerializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..82d1e5939f290dd9af69860dd594d925a7110fa7 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceSerializerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class LinkedResourceSerializerTest { + + @DisplayName("Test the json serilization of linked resource annotations") + @Nested + class TestSerialization { + @Test + void shouldSerialize() throws JsonProcessingException { + var testObj = new LinkedResourceTestObject(UserProfileTestFactory.ID); + + String serialized = new ObjectMapper().writeValueAsString(testObj); + + assertThat(serialized).isEqualTo("{\"id\":\"/api/vorgangs/" + UserProfileTestFactory.ID.toString() + "\"}"); + } + + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceTestObject.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceTestObject.java new file mode 100644 index 0000000000000000000000000000000000000000..9d35983568649937ebec6347946b4c7115995ab3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedResourceTestObject.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.vorgang.VorgangController; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +class LinkedResourceTestObject { + @LinkedResource(controllerClass = VorgangController.class) + private UserId id; +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2ac3952751a60947a4ecc4eb44595934d4a87a7e --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceDeserializerTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.exc.StreamReadException; +import com.fasterxml.jackson.databind.DatabindException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class LinkedUserProfileResourceDeserializerTest { + private static final String TEST_JSON = "{\"id\":\"http://localhost/api/profile/" + UserProfileTestFactory.ID.toString() + "\"}"; + + @DisplayName("Test the deserilization of linked resource json") + @Nested + class TestDeserialization { + @Test + void shouldDeserialize() throws StreamReadException, DatabindException, IOException { + LinkedUserProfileResourceTestObject res = new ObjectMapper().readValue(TEST_JSON.getBytes(), LinkedUserProfileResourceTestObject.class); + + assertThat(res).hasFieldOrPropertyWithValue("id", UserProfileTestFactory.ID); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ec0f071f36690e3a38dde89d2825df311319ad6b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceSerializerTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class LinkedUserProfileResourceSerializerTest { + @DisplayName("Test the json serilization of linked user profile resource annotations") + @Nested + class TestSerialization { + private static final String HTTP_LOCALHOST = "http://localhost/"; + private static final String API_TEMPLATE = "api/profile/%s"; + private static final String API_PATH = "api/profile/"; + + private UserProfileUrlProvider provider = new UserProfileUrlProvider(); + + @Mock + private ApplicationContext context; + @Mock + private Environment env; + + @Test + void shouldSerialize() throws JsonProcessingException { + when(env.getProperty(UserProfileUrlProvider.URL_ROOT_KEY)).thenReturn(HTTP_LOCALHOST); + when(env.getProperty(UserProfileUrlProvider.USER_PROFILES_TEMPLATE_KEY)).thenReturn(API_TEMPLATE); + when(context.getEnvironment()).thenReturn(env); + provider.setApplicationContext(context); + + var testObj = new LinkedUserProfileResourceTestObject(UserProfileTestFactory.ID); + + String serialized = new ObjectMapper().writeValueAsString(testObj); + + assertThat(serialized).isEqualTo("{\"id\":\"" + HTTP_LOCALHOST + API_PATH + UserProfileTestFactory.ID.toString() + "\"}"); + } + + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceTestObject.java b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceTestObject.java new file mode 100644 index 0000000000000000000000000000000000000000..f97355ad439c33e27de8a3056742130ba1c8e4a6 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/LinkedUserProfileResourceTestObject.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import de.itvsh.goofy.common.user.UserId; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +class LinkedUserProfileResourceTestObject { + @LinkedUserProfileResource + private UserId id; +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/ModelBuilderTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/ModelBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..97a15a5e6898d2c1d2358a058db56aadfc0f8392 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/ModelBuilderTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.RequestMapping; + +import lombok.Builder; + +class ModelBuilderTest { + + @DisplayName("Add link by annotation if missing") + @Nested + class TestAddLinkByAnnotationIfMissing { + + private static final String USER_MANAGER_URL = "http://localhost"; + private static final String USER_MANAGER_PROFILE_TEMPLATE = "/api/profile/%s"; + + private UserProfileUrlProvider provider = new UserProfileUrlProvider(); + + @Mock + private ApplicationContext context; + @Mock + private Environment env; + + private TestEntity entity = TestEntityTestFactory.create(); + + @BeforeEach + void mockEnvironment() { + when(env.getProperty(UserProfileUrlProvider.URL_ROOT_KEY)).thenReturn(USER_MANAGER_URL); + when(env.getProperty(UserProfileUrlProvider.USER_PROFILES_TEMPLATE_KEY)).thenReturn(USER_MANAGER_PROFILE_TEMPLATE); + when(context.getEnvironment()).thenReturn(env); + + provider.setApplicationContext(context); + } + + @Test + void shouldHaveAddLinkByLinkedResource() { + var model = ModelBuilder.fromEntity(entity).buildModel(); + + assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE); + } + + @Test + void shouldHaveAddLinkByLinkedUserProfileResource() { + var model = ModelBuilder.fromEntity(entity).buildModel(); + + assertThat(model.getLink(TestController.USER_REL).get().getHref()) + .isEqualTo(String.format(USER_MANAGER_URL + USER_MANAGER_PROFILE_TEMPLATE, TestEntityTestFactory.USER)); + } + } + + @DisplayName("if usermanager is not configured") + @Nested + class TestNotAddLinkByAnnotationIfNotConfigured { + + private UserProfileUrlProvider provider = new UserProfileUrlProvider(); + + @Mock + private ApplicationContext context; + @Mock + private Environment env; + + private TestEntity entity = TestEntityTestFactory.create(); + + @BeforeEach + void mockEnvironment() { + when(env.getProperty(UserProfileUrlProvider.URL_ROOT_KEY)).thenReturn(null); + when(context.getEnvironment()).thenReturn(env); + + provider.setApplicationContext(context); + } + + @Test + void shouldHaveAddLinkByLinkedResource() { + var model = ModelBuilder.fromEntity(entity).buildModel(); + + assertThat(model.getLink(TestController.FILE_REL).get().getHref()).isEqualTo(TestController.PATH + "/" + TestEntityTestFactory.FILE); + } + + @Test + void shouldNotHaveLinkAddByLinkedUserProfileAnnotation() { + var model = ModelBuilder.fromEntity(entity).buildModel(); + + assertThat(model.getLink(TestController.USER_REL)).isNotPresent(); + } + } +} + +@Builder +class TestEntity { + + @LinkedResource(controllerClass = TestController.class) + private String file; + + @LinkedUserProfileResource + private String user; +} + +@RequestMapping(TestController.PATH) +class TestController { + + static final String PATH = "/api/test"; + + static final String USER_REL = "user"; + static final String FILE_REL = "file"; + +} + +class TestEntityTestFactory { + + static final String USER = UUID.randomUUID().toString(); + static final String FILE = UUID.randomUUID().toString(); + + public static TestEntity create() { + return TestEntity.builder() + .file(FILE) + .user(USER) + .build(); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/RegexUtilTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/RegexUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..265f9e200cc7eaa376ccb34b19fac14ac395348c --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/RegexUtilTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.assertj.core.api.Assertions.*; + +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class RegexUtilTest { + + @Nested + class TestValidationEMail { + + private final String REGEX = RegexUtil.VALIDATION_EMAIL; + + @Nested + class TestEmailAt { + + @Test + void shouldMatchOnOneAtChar() { + shouldMatch("localpart@domain.com"); + } + + @Test + void shouldNotMatchOnNoneAtChar() { + shouldNotMatch("localpartdomain.com"); + } + + @Test + void shouldNotMatchOnMultipleAtChar() { + shouldNotMatch("localpart@@domain.com"); + } + } + + @Nested + class TestDomain { + + @Nested + class TestDomainDot { + + @Test + void shouldMatchOnOneDot() { + shouldMatch("localpart@domain.com"); + } + + @Test + void shouldNotMatchOnNoneDot() { + shouldNotMatch("localpart@domaincom"); + } + + @Test + void shouldMatchOnMultipleDots() { + shouldNotMatch("localpart@domain...com"); + } + } + + @Nested + class TestDomainCharCaseSensitive { + + @Test + void shouldMatchOnLowerCaseChar() { + shouldMatch("localpart@domain.com"); + } + + @Test + void shouldMatchOnUpperCaseChar() { + shouldMatch("localpart@DOMAIN.COM"); + } + } + } + + @Nested + class TestLocalPart { + + @Nested + class TestLocalPartChar { + + @Test + void shouldMatchOnAtLeastOneChar() { + shouldMatch("l@domain.com"); + } + + @Test + void shouldMatchOnNoneChar() { + shouldNotMatch("@domain.com"); + } + } + + @Nested + class TestLocalPartCharCaseSensitive { + + @Test + void shouldAllowUpperCaseChar() { + shouldMatch("UPPER_CHAR@domain.com"); + } + + @Test + void shouldAllowLowerCaseChar() { + shouldMatch("lower_char@domain.com"); + } + } + } + + @Test + void shouldAcceptCharInLocalPart() { + shouldMatch("local-part-1234567890!#$%&'*+/=?^_`{|}~-.'/&%*@domain.com"); + } + + private void shouldMatch(String eMail) { + boolean match = matchPattern(eMail); + + assertThat(match).isTrue(); + } + + private void shouldNotMatch(String eMail) { + boolean match = matchPattern(eMail); + + assertThat(match).isFalse(); + } + + private boolean matchPattern(String eMail) { + return Pattern.matches(REGEX, eMail); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/UserProfileUrlProviderTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/UserProfileUrlProviderTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..22c24b24229f915b64b1720a5cf64f7337572133 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/UserProfileUrlProviderTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common; + +import static org.mockito.Mockito.*; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +public class UserProfileUrlProviderTestFactory { + public static final String ROOT_URL = "https://localhost"; + public static final String USER_PROFILES_API_PATH = "/api/userProfiles/"; + public static final String KOP_USER_MANAGER_PROFILE_TEMPLATE = "kop.user-manager.profile-template"; + public static final String KOP_USER_MANAGER_URL = "kop.user-manager.url"; + + public static void initUserProfileUrlProvider(UserProfileUrlProvider urlProvider) { + ApplicationContext context = mock(ApplicationContext.class); + Environment environment = mock(Environment.class); + when(environment.getProperty(KOP_USER_MANAGER_URL)).thenReturn(ROOT_URL); + when(environment.getProperty(KOP_USER_MANAGER_PROFILE_TEMPLATE)).thenReturn(USER_PROFILES_API_PATH + "%s"); + when(context.getEnvironment()).thenReturn(environment); + urlProvider.setApplicationContext(context); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..29653aded651c4b6e20d585c0d07a781c9d47fd0 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemServiceTest.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.attacheditem; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandBodyMapper; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.kommentar.Kommentar; +import de.itvsh.goofy.kommentar.KommentarTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.wiedervorlage.Wiedervorlage; +import de.itvsh.goofy.wiedervorlage.WiedervorlageTestFactory; + +class VorgangAttachedItemServiceTest { + + @Spy + @InjectMocks + private VorgangAttachedItemService service; + @Mock + private CommandBodyMapper commandBodyMapper; + @Mock + private CommandService commandService; + + @DisplayName("Create new wiedervorlage") + @Nested + class TestCreateNewWiedervorlage { + + private Wiedervorlage wiedervorlage = WiedervorlageTestFactory.create(); + + @BeforeEach + void mockServices() { + doReturn(VorgangAttachedItemTestFactory.create()).when(service).buildVorgangAttachedItem(any(), any(), any()); + doReturn(CommandTestFactory.createCreateCommand()).when(service).buildCreateCommand(any(), any()); + } + + @Test + void shouldBuildVorgangAttachedItem() { + callCreateNewWiedervorlage(); + + verify(service).buildVorgangAttachedItem(wiedervorlage, VorgangHeaderTestFactory.ID, VorgangAttachedItemService.WIEDERVORLAGE_ITEM_NAME); + } + + @Test + void shouldBuildCreateCommand() { + callCreateNewWiedervorlage(); + + verify(service).buildCreateCommand(eq(VorgangHeaderTestFactory.ID), any(VorgangAttachedItem.class)); + } + + @Test + void shouldCallCommandService() { + callCreateNewWiedervorlage(); + + verify(commandService).createCommand(any(CreateCommand.class)); + } + + private Command callCreateNewWiedervorlage() { + return service.createNewWiedervorlage(wiedervorlage, VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Edit wiedervorlage") + @Nested + class TestEditWiedervorlage { + + private Wiedervorlage wiedervorlage = WiedervorlageTestFactory.create(); + + @BeforeEach + void mockService() { + doReturn(VorgangAttachedItemTestFactory.create()).when(service).buildVorgangAttachedItem(any(), any(), any()); + doReturn(CommandTestFactory.createCreateCommand()).when(service).buildUpdateCommand(any(), any(), any()); + } + + @Test + void shouldBuildVorgangAttachedItem() { + callEditWiedervorlage(); + + verify(service).buildVorgangAttachedItem(wiedervorlage, VorgangHeaderTestFactory.ID, VorgangAttachedItemService.WIEDERVORLAGE_ITEM_NAME); + } + + @Test + void shouldBuildCreateCommand() { + callEditWiedervorlage(); + + verify(service).buildUpdateCommand(eq(VorgangHeaderTestFactory.ID), eq(WiedervorlageTestFactory.ID), any(VorgangAttachedItem.class)); + } + + @Test + void shouldCallCommandService() { + callEditWiedervorlage(); + + verify(commandService).createCommand(any(CreateCommand.class), eq(WiedervorlageTestFactory.VERSION)); + } + + private Command callEditWiedervorlage() { + return service.editWiedervorlage(wiedervorlage, wiedervorlage.getId(), wiedervorlage.getVersion()); + } + } + + @Nested + class TestWiedervorlageDone { + private Wiedervorlage wiedervorlage = WiedervorlageTestFactory.create(); + + @Test + void shouldCallCommandService() { + callSetWiedervorlageDone(true); + + verify(commandService).createCommand(any(CreateCommand.class), eq(WiedervorlageTestFactory.VERSION)); + } + + @Test + void shouldSetItemName() { + CreateCommand command = service.createPatchCommand(WiedervorlageTestFactory.create(), false); + + assertThat(((VorgangAttachedItem) command.getBody()).getItemName()).isEqualTo(Wiedervorlage.class.getSimpleName()); + } + + @Test + void shouldSetItem() { + CreateCommand command = service.createPatchCommand(WiedervorlageTestFactory.create(), false); + + assertThat(((VorgangAttachedItem) command.getBody()).getItem()).isNotEmpty(); + } + + @Test + void shouldSetItemDoneValue() { + CreateCommand command = service.createPatchCommand(WiedervorlageTestFactory.create(), true); + + assertThat(((VorgangAttachedItem) command.getBody()).getItem()).containsEntry("done", true); + } + + private Command callSetWiedervorlageDone(boolean done) { + return service.setWiedervorlageDone(wiedervorlage, done); + } + + } + + @DisplayName("Create new kommentar") + @Nested + class TestCreateNewKommentar { + + private Kommentar kommentar = KommentarTestFactory.create(); + + @BeforeEach + void mockServices() { + doReturn(VorgangAttachedItemTestFactory.create()).when(service).buildVorgangAttachedItem(any(), any(), any()); + doReturn(CommandTestFactory.createCreateCommand()).when(service).buildCreateCommand(any(), any()); + } + + @Test + void shouldBuildVorgangAttachedItem() { + callCreateNewKommentar(); + + verify(service).buildVorgangAttachedItem(kommentar, VorgangHeaderTestFactory.ID, VorgangAttachedItemService.KOMMENTAR_ITEM_NAME); + } + + @Test + void shouldBuildCreateCommand() { + callCreateNewKommentar(); + + verify(service).buildCreateCommand(eq(VorgangHeaderTestFactory.ID), any(VorgangAttachedItem.class)); + } + + @Test + void shouldCallCommandService() { + callCreateNewKommentar(); + + verify(commandService).createCommand(any(CreateCommand.class)); + } + + private Command callCreateNewKommentar() { + return service.createNewKommentar(kommentar, VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Edit kommentar") + @Nested + class TestEditKommentar { + + private Kommentar kommentar = KommentarTestFactory.create(); + + @BeforeEach + void mockService() { + doReturn(VorgangAttachedItemTestFactory.create()).when(service).buildVorgangAttachedItem(any(), any(), any()); + doReturn(CommandTestFactory.createCreateCommand()).when(service).buildUpdateCommand(any(), any(), any()); + } + + @Test + void shouldBuildVorgangAttachedItem() { + callEditKommentar(); + + verify(service).buildVorgangAttachedItem(kommentar, VorgangHeaderTestFactory.ID, VorgangAttachedItemService.KOMMENTAR_ITEM_NAME); + } + + @Test + void shouldBuildCreateCommand() { + callEditKommentar(); + + verify(service).buildUpdateCommand(eq(VorgangHeaderTestFactory.ID), eq(KommentarTestFactory.ID), any(VorgangAttachedItem.class)); + } + + @Test + void shouldCallCommandService() { + callEditKommentar(); + + verify(commandService).createCommand(any(CreateCommand.class), eq(KommentarTestFactory.VERSION)); + } + + private Command callEditKommentar() { + return service.editKommentar(kommentar, KommentarTestFactory.ID, KommentarTestFactory.VERSION); + } + } + + @DisplayName("build vorgang attached item") + @Nested + class TestBuildVorgangAttachedItem { + + private static final Wiedervorlage BODY = WiedervorlageTestFactory.create(); + + @BeforeEach + void mockServices() { + when(commandBodyMapper.fromObjectToMap(any())).thenReturn(VorgangAttachedItemTestFactory.ITEM); + } + + @Test + void shouldCallCommandBodyMapper() { + buildVorgangAttachedItem(); + + verify(commandBodyMapper).fromObjectToMap(BODY); + } + + @Test + void shouldContainsVorgangId() { + var vorgangAttachedItem = buildVorgangAttachedItem(); + + assertThat(vorgangAttachedItem.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldContainsItemName() { + var vorgangAttachedItem = buildVorgangAttachedItem(); + + assertThat(vorgangAttachedItem.getItemName()).isEqualTo(VorgangAttachedItemService.WIEDERVORLAGE_ITEM_NAME); + } + + @Test + void shouldContainsItem() { + var vorgangAttachedItem = buildVorgangAttachedItem(); + + assertThat(vorgangAttachedItem.getItem()).isEqualTo(VorgangAttachedItemTestFactory.ITEM); + } + + private VorgangAttachedItem buildVorgangAttachedItem() { + return service.buildVorgangAttachedItem(BODY, VorgangHeaderTestFactory.ID, + VorgangAttachedItemService.WIEDERVORLAGE_ITEM_NAME); + } + } + + @DisplayName("build createCommand") + @Nested + class TestBuildCreateCommand { + + @Test + void shouldContainsVorgangId() throws Exception { + var createCommand = buildCommand(); + + assertThat(createCommand.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldContainsRelationId() throws Exception { + var createCommand = buildCommand(); + + assertThat(createCommand.getRelationId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldContainsOrder() throws Exception { + var createCommand = buildCommand(); + + assertThat(createCommand.getOrder()).isEqualTo(CommandOrder.CREATE_ATTACHED_ITEM); + } + + @Test + void shouldContainsBody() throws Exception { + var createCommand = buildCommand(); + + assertThat(((VorgangAttachedItem) createCommand.getBody())).usingRecursiveComparison() + .isEqualTo(VorgangAttachedItemTestFactory.create()); + } + + private CreateCommand buildCommand() { + return service.buildCreateCommand(VorgangHeaderTestFactory.ID, VorgangAttachedItemTestFactory.create()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f689e4ac5cf2c26c53a1c8d03812203b3ea43679 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/attacheditem/VorgangAttachedItemTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.attacheditem; + +import java.util.Map; +import java.util.UUID; + +import de.itvsh.goofy.common.attacheditem.VorgangAttachedItem; + +public class VorgangAttachedItemTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final long VERSION = 42; + public static final String CLIENT = "Goofy"; + public static final String ITEM_NAME = "ItemName"; + public static final Map<String, Object> ITEM = Map.of("oneKey", "oneValue"); + + public static VorgangAttachedItem create() { + return createBuilder().build(); + } + + public static VorgangAttachedItem.VorgangAttachedItemBuilder createBuilder() { + return VorgangAttachedItem.builder() + .id(ID) + .version(VERSION) + .client(CLIENT) + .itemName(ITEM_NAME) + .item(ITEM); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..3db4f520879715d0c6b8cb40bd84863f7223e17a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerITCase.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static de.itvsh.goofy.JwtTokenUtil.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.downloadtoken.DownloadTokenController; +import de.itvsh.goofy.common.downloadtoken.DownloadTokenProperties; +import de.itvsh.goofy.common.downloadtoken.DownloadTokenTestFactory; +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import io.jsonwebtoken.JwtBuilder; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class BinaryFileControllerITCase { + + @SpyBean + private BinaryFileController controller; + @MockBean + private BinaryFileService service; + @Autowired + private MockMvc mockMvc; + @Autowired + private DownloadTokenProperties downloadTokenProperties; + + private FileId fileId = FileId.createNew(); + + @Nested + class TestDragAndDropFlow { + + private JwtBuilder tokenBuilder = DownloadTokenTestFactory.createTokenBuilder(downloadTokenProperties.getSecret(), + downloadTokenProperties.getValidity()); + + @BeforeEach + void init() { + when(service.getFile(any())).thenReturn(OzgFileTestFactory.create()); + } + + @Test + void shouldGet403WhenNoFileId() throws Exception { + setTokenToSecuriyContext(tokenBuilder.compact()); + + performRequest().andExpect(status().isForbidden()); + } + + @Test + void shouldGet403WhenWrongFileId() throws Exception { + setTokenToSecuriyContext(tokenBuilder.addClaims(createClaims(FileId.createNew())).compact()); + + performRequest().andExpect(status().isForbidden()); + } + + @Test + void shouldGetFile() throws Exception { + setTokenToSecuriyContext(tokenBuilder.addClaims(createClaims(fileId)).compact()); + + performRequest().andExpect(status().isOk()); + } + + private Map<String, Object> createClaims(FileId fileId) { + return new HashMap<>(Map.of( + FIRSTNAME_CLAIM, UserProfileTestFactory.FIRSTNAME, + LASTNAME_CLAIM, UserProfileTestFactory.LASTNAME, + ROLE_CLAIM, List.of(), + FILEID_CLAIM, fileId.toString())); + } + + void setTokenToSecuriyContext(String token) throws Exception { + mockMvc.perform(get(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token)) + .andExpect(status().isOk()); + } + } + + @Nested + class TestDefaultFlow { + + @Mock + private Authentication authentication; + + @BeforeEach + void init() { + when(service.getFile(any())).thenReturn(OzgFileTestFactory.create()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + @Test + void shouldGet401WhenUnautorised() throws Exception { + when(authentication.isAuthenticated()).thenReturn(Boolean.FALSE); + + performRequest().andExpect(status().isUnauthorized()); + } + + @Test + void shouldGetFile() throws Exception { + when(authentication.isAuthenticated()).thenReturn(Boolean.TRUE); + + performRequest().andExpect(status().isOk()); + } + } + + ResultActions performRequest() throws Exception { + return mockMvc.perform(get(BinaryFileController.PATH + "/" + fileId.toString())); + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6583a3af5970b952b238ac765a4c0bc9b55126f8 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileControllerTest.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.kop.common.errorhandling.TechnicalException; + +class BinaryFileControllerTest { + + private final String PATH = BinaryFileController.PATH; + + @Spy + @InjectMocks + private BinaryFileController controller; + @Mock + private BinaryFileService service; + @Mock + private BinaryFileModelAssembler modelAssembler; + @Mock + private FileIdMapper fileIdMapper; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestUploadFile { + + private CompletableFuture<FileId> fileIdFuture = CompletableFuture.completedFuture(BinaryFileTestFactory.FILE_ID); + + @BeforeEach + void mockServiec() { + when(service.uploadFile(any())).thenReturn(fileIdFuture); + } + + @Test + void shouldCallService() throws Exception { + var uploadBinaryFile = UploadBinaryFileTestFactory.create(); + doReturn(uploadBinaryFile).when(controller).buildBinaryFileUploadRequest(any(), any(), any()); + + callEndpoint(); + + verify(service).uploadFile(uploadBinaryFile); + } + + @Test + void shouldReturnCreatedStatus() throws Exception { + var result = callEndpoint(); + + mockMvc.perform(asyncDispatch(result)).andExpect(status().isCreated()); + } + + @Test + void shouldReturnLocation() throws Exception { + var result = callEndpoint(); + + mockMvc.perform(asyncDispatch(result)) + .andExpect(header().string("Location", containsString("/api/binaryFiles/" + BinaryFileTestFactory.FILE_ID))); + } + + private MvcResult callEndpoint() throws Exception { + return mockMvc.perform(multipart(PATH + "/" + VorgangHeaderTestFactory.ID + "/" + BinaryFileTestFactory.FIELD + "/file") + .file(BinaryFileTestFactory.TEST_FILE)) + .andExpect(request().asyncStarted()) + .andReturn(); + } + } + + @Nested + class TestBuildBinaryFileUploadRequest { + + @Test + void shouldContainsVorgangId() { + var result = buildBinaryFileUploadRequest(); + + assertThat(result.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldContainsField() { + var result = buildBinaryFileUploadRequest(); + + assertThat(result.getField()).isEqualTo(BinaryFileTestFactory.FIELD); + } + + @Test + void shouldContainsSize() { + var result = buildBinaryFileUploadRequest(); + + assertThat(result.getSize()).isEqualTo(OzgFileTestFactory.SIZE); + } + + @Test + void shouldContainsName() { + var result = buildBinaryFileUploadRequest(); + + assertThat(result.getName()).isEqualTo(OzgFileTestFactory.NAME); + } + + @Test + void shouldContainsContentType() { + var result = buildBinaryFileUploadRequest(); + + assertThat(result.getContentType()).isEqualTo(OzgFileTestFactory.CONTENT_TYPE); + } + + @Test + void shouldGetInputStreamFromFile() { + buildBinaryFileUploadRequest(); + + verify(controller).getInputStream(BinaryFileTestFactory.TEST_FILE); + } + + private UploadBinaryFileRequest buildBinaryFileUploadRequest() { + return controller.buildBinaryFileUploadRequest(VorgangHeaderTestFactory.ID, BinaryFileTestFactory.FIELD, BinaryFileTestFactory.TEST_FILE); + } + } + + @Nested + class TestGetInputStreamOfFile { + + @Mock + private MultipartFile file; + + @Test + void shouldReturnStream() throws IOException { + var uploadStream = file.getInputStream(); + + when(file.getInputStream()).thenReturn(uploadStream); + + assertThat(controller.getInputStream(file)).isEqualTo(uploadStream); + } + + @Test + void shouldThrowException() throws IOException { + when(file.getInputStream()).thenThrow(IOException.class); + + assertThrows(TechnicalException.class, () -> controller.getInputStream(file)); + } + } + + @Nested + class TestGetFileData { + + @Mock + private StreamingResponseBody body; + private OzgFile ozgFile = OzgFileTestFactory.create(); + + @BeforeEach + void mockService() { + when(service.getFile(any())).thenReturn(ozgFile); + } + + @Test + void shouldCallServiceGetFile() throws Exception { + callEndpoint(); + + verify(service).getFile(OzgFileTestFactory.ID); + } + + @Test + void shouldBuildResponseEntity() throws Exception { + doReturn(body).when(controller).createDownloadStreamingBody(OzgFileTestFactory.ID); + + callEndpoint(); + + verify(controller).buildResponseEntity(ozgFile, body); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(PATH + "/" + OzgFileTestFactory.ID).contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andExpect(status().isOk()); + } + } + + @Nested + class TestCreateDownloadStreamingBody { + + @Mock + private OutputStream out; + + @Test + void shouldCallServiceWriteFileContent() throws IOException { + controller.createDownloadStreamingBody(OzgFileTestFactory.ID).writeTo(out); + + verify(service).writeFileContent(any(), any()); + } + } + + @Nested + class TestGetFile { + + private OzgFile ozgFile = OzgFileTestFactory.create(); + + @BeforeEach + void mockServiec() { + when(service.getFile(any())).thenReturn(ozgFile); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).getFile(BinaryFileTestFactory.FILE_ID); + } + + @Test + void shouldCallAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toModel(ozgFile); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(BinaryFileController.PATH + '/' + BinaryFileTestFactory.FILE_ID) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + } + } + + @Nested + class TestGetFiles { + + private OzgFile file = OzgFileTestFactory.create(); + + @BeforeEach + void mockServiec() { + when(service.getFiles(any())).thenReturn(Stream.of(file)); + when(fileIdMapper.toString(any())).then(fileId -> fileId.toString()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).getFiles(any()); + } + + @Test + void shouldCallAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toCollectionModel(Stream.of(file).collect(Collectors.toList())); + } + + private CollectionModel<EntityModel<OzgFile>> callEndpoint() throws Exception { + return controller.getFiles(List.of(BinaryFileTestFactory.FILE_ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserverTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserverTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a8eda9af859bf3138a05502970747e3de4d66070 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileDownloadStreamObserverTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import com.google.protobuf.ByteString; + +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataResponse; + +class BinaryFileDownloadStreamObserverTest { + + @InjectMocks + private BinaryFileDownloadStreamObserver downloadStreamObserver; + @Mock + private CompletableFuture<Boolean> streamFuture; + @Mock + private OutputStream out; + + @Nested + class TestOnNext { + + @Mock + private GrpcGetBinaryFileDataResponse response; + + @Test + void shouldWriteToStreamOnReceivingContentPart() throws IOException { + downloadStreamObserver.onNext(createFileConentResponse()); + + verify(out).write(BinaryFileTestFactory.DATA); + } + + private GrpcGetBinaryFileDataResponse createFileConentResponse() { + return GrpcGetBinaryFileDataResponse.newBuilder().setFileContent(ByteString.copyFrom(BinaryFileTestFactory.DATA)).build(); + } + } + + @Nested + class TestOnError { + + @Mock + private Throwable throwable; + + @Test + void shouldCompleteFutureExceptionally() { + downloadStreamObserver.onError(throwable); + + verify(streamFuture).completeExceptionally(throwable); + } + } + + @Nested + class TestOnCompleted { + + @Test + void shouldCompleteFuture() { + downloadStreamObserver.onCompleted(); + + verify(streamFuture).complete(true); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..e9566fb44e5ca833244f4b64d11bd4bf29328a24 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileITCase.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.util.unit.DataSize; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class BinaryFileITCase { + + @MockBean + private BinaryFileRemoteService remoteService; + + @Autowired + private MockMvc mockMvc; + + @WithMockUser + @Nested + class TestUploadFile { + + private final String SINGLE_PATH = BinaryFileController.PATH + "/{vorgangId}/{field}/file"; + private final CompletableFuture<FileId> future = CompletableFuture.completedFuture(BinaryFileTestFactory.FILE_ID); + + public static final byte[] DATA_OVER_3_MB = BinaryFileTestFactory.createByteOfSize(DataSize.ofMegabytes(41)); + public static final MockMultipartFile TEST_FILE = new MockMultipartFile("file", BinaryFileTestFactory.NAME, + BinaryFileTestFactory.CONTENT_TYPE, DATA_OVER_3_MB); + + @BeforeEach + void mockStub() { + when(remoteService.uploadFile(any())).thenReturn(future); + } + + @Nested + class TestDefault { + @Test + void shouldReturnConstraintViolationException() throws Exception { + var result = callEndpoint(TEST_FILE, BinaryFileTestFactory.FIELD); + + result.andExpect(status().isUnprocessableEntity()); + } + + @Test + void shouldReturnConstraintViolationExceptionData() throws Exception { + var result = callEndpoint(TEST_FILE, BinaryFileTestFactory.FIELD); + + result.andExpect(jsonPath("issues[0].field").value("uploadBinaryFileRequest")) + .andExpect(jsonPath("issues[0].messageCode").value("validation_field_file_size_exceeded")) + .andExpect(jsonPath("issues[0].message").value("validation_field_file_size_exceeded")) + .andExpect(jsonPath("issues[0].parameters[0].name").value("unit")) + .andExpect(jsonPath("issues[0].parameters[0].value").value(UploadBinaryFileSizeValidator.DEFAULT_UNIT_STRING)); + } + + @Test + void shouldContainsExceptionDataOnDefaultMaxSize() throws Exception { + var result = callEndpoint(TEST_FILE, BinaryFileTestFactory.FIELD); + + result.andExpect(jsonPath("issues[0].parameters[1].name").value("max")) + .andExpect(jsonPath("issues[0].parameters[1].value").value("40")); + } + } + + @Nested + class TestForPostfachNachricht { + + private final static String FIELD = "postfachNachrichtAttachment"; + + @Test + void shouldContainsExceptionDataOnPostfachNachrichtMaxSize() throws Exception { + var result = callEndpoint(TEST_FILE, FIELD); + + result.andExpect(jsonPath("issues[0].parameters[1].name").value("max")) + .andExpect(jsonPath("issues[0].parameters[1].value").value("3")); + } + } + + private ResultActions callEndpoint(MockMultipartFile file, String field) throws Exception { + return mockMvc.perform(multipart(SINGLE_PATH, VorgangHeaderTestFactory.ID, field).file(file)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..16fc548badf4ac12f9dad6d9af970f9cb723d67b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileModelAssemblerTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.springframework.hateoas.IanaLinkRelations; + +import de.itvsh.goofy.common.file.OzgFileTestFactory; + +class BinaryFileModelAssemblerTest { + + @InjectMocks // NOSONAR + private BinaryFileModelAssembler modelAssembler; + + @Nested + class TestLinksOnModel { + + @Test + void shouldHaveSelfLink() { + var link = modelAssembler.toModel(OzgFileTestFactory.create()).getLink(IanaLinkRelations.SELF_VALUE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(BinaryFileController.PATH + "/" + OzgFileTestFactory.ID); + } + + @Test + void shouldHaveDownloadLink() { + var link = modelAssembler.toModel(OzgFileTestFactory.create()).getLink(BinaryFileModelAssembler.REL_DOWNLOAD); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(BinaryFileController.PATH + "/" + OzgFileTestFactory.ID); + } + } + + @Nested + class TestLinksOnCollectionModel { + + @Test + void shouldHaveSelfLink() { + var link = modelAssembler.toCollectionModel(Stream.of(OzgFileTestFactory.create())).getLink(IanaLinkRelations.SELF_VALUE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(BinaryFileController.PATH); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..468c85eddc6f11738f005f195f8c84026fbf34e5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileRemoteServiceTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.OutputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.google.protobuf.ByteString; + +import de.itvsh.goofy.common.GrpcCallContextTestFactory; +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.common.file.GrpcOzgFileTestFactory; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileMapper; +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.ozg.pluto.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcFindFilesResponse; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcGetBinaryFileDataRequest; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; +import io.grpc.stub.CallStreamObserver; + +class BinaryFileRemoteServiceTest { + + @Spy + @InjectMocks + private BinaryFileRemoteService remoteService; + @Mock + private BinaryFileServiceBlockingStub serviceStub; + @Mock + private BinaryFileServiceStub serviceAsyncStub; + @Mock + private ContextService contextService; + @Mock + private OzgFileMapper ozgFileMapper; + @Mock + private FileIdMapper fileIdMapper; + + private GrpcCallContext callContext = GrpcCallContextTestFactory.create(); + + @BeforeEach + void mockContextService() { + lenient().when(contextService.createCallContext()).thenReturn(callContext); + } + + @Nested + class TestUploadBinaryFile { + + @Mock + private CallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver; + @Mock + private BinaryFileUploadStreamObserver responseObserver; + private UploadBinaryFileRequest uploadBinaryFile = UploadBinaryFileTestFactory.create(); + + @BeforeEach + void initMocks() { + when(serviceAsyncStub.uploadBinaryFileAsStream(any())).thenReturn(requestObserver); + } + + @Test + void shouldCreateStreamObserver() { + callService(); + + verify(remoteService).createUploadBinaryFileObserver(ArgumentMatchers.<CompletableFuture<FileId>>any(), any()); + verify(remoteService).buildMetaDataRequest(uploadBinaryFile); + } + + @Test + void shouldCallUploadBinaryFileOnServiceAsyncStub() { + doReturn(responseObserver).when(remoteService).createUploadBinaryFileObserver(any(), any()); + + callService(); + + verify(serviceAsyncStub).uploadBinaryFileAsStream(responseObserver); + } + + private void callService() { + remoteService.uploadFile(uploadBinaryFile); + } + } + + @Nested + class TestBuildMetaDataRequest { + + @Test + void shouldCallContextService() { + callService(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldContainsVorgangId() { + var metaData = callService(); + + assertThat(metaData.getVorgangId()).isEqualTo(UploadBinaryFileTestFactory.VORGANG_ID); + } + + @Test + void shouldContainsFieldName() { + var metaData = callService(); + + assertThat(metaData.getField()).isEqualTo(UploadBinaryFileTestFactory.FIELD); + } + + @Test + void shouldContainsContentType() { + var metaData = callService(); + + assertThat(metaData.getContentType()).isEqualTo(OzgFileTestFactory.CONTENT_TYPE); + + } + + @Test + void shouldContainsSize() { + var metaData = callService(); + + assertThat(metaData.getSize()).isEqualTo(OzgFileTestFactory.SIZE); + } + + @Test + void shouldContainsFileName() { + var metaData = callService(); + + assertThat(metaData.getFileName()).isEqualTo(OzgFileTestFactory.NAME); + } + + private GrpcUploadBinaryFileMetaData callService() { + return remoteService.buildMetaDataRequest(UploadBinaryFileTestFactory.create()).getMetadata(); + } + } + + @Nested + class TestBuildChunkRequest { + + @Test + void shouldContainContent() { + var chunkRequest = remoteService.buildChunkRequest(BinaryFileTestFactory.DATA); + + assertThat(chunkRequest.getFileContent()).isEqualTo(ByteString.copyFrom(BinaryFileTestFactory.DATA)); + } + } + + @Nested + class TestGetFile { + + private final GrpcFindFilesResponse response = BinaryFileTestFactory.createFindBinaryFilesResponse(); + + @BeforeEach + void mock() { + when(serviceStub.findBinaryFilesMetaData(any())).thenReturn(response); + } + + @Test + void shouldCallServiceStub() { + doServiceCall(); + + verify(serviceStub).findBinaryFilesMetaData(any()); + } + + @Test + void shouldCallContextService() { + doServiceCall(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallMapper() { + doServiceCall(); + + verify(ozgFileMapper).toFile(GrpcOzgFileTestFactory.create()); + } + + private OzgFile doServiceCall() { + return remoteService.getFile(BinaryFileTestFactory.FILE_ID); + } + } + + @Nested + class TestGetFileData { + + @Mock + private OutputStream out; + @Mock + private BinaryFileDownloadStreamObserver responseObserver; + private String fileId = OzgFileTestFactory.ID.toString(); + + @BeforeEach + void mockStreamObserver() { + doReturn(responseObserver).when(remoteService).createDownloadBinaryFileObserver(any(), any()); + when(fileIdMapper.toString(any())).thenReturn(fileId); + doNothing().when(remoteService).waitUntilFutureToComplete(any()); + } + + @Test + void shouldCreateDownloadBinaryFileObserver() { + callService(); + + verify(remoteService).createDownloadBinaryFileObserver(ArgumentMatchers.<CompletableFuture<Boolean>>any(), eq(out)); + } + + @Test + void shouldCallContextService() { + callService(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallFileIdMapper() { + callService(); + + verify(fileIdMapper).toString(OzgFileTestFactory.ID); + } + + @Test + void shouldCallGetFileDataOnAsyncServiceStub() { + callService(); + + verify(serviceAsyncStub).getBinaryFileContent(any(GrpcGetBinaryFileDataRequest.class), eq(responseObserver)); + } + + @Test + void shouldWaitUntilFutureToComplete() { + callService(); + + verify(remoteService).waitUntilFutureToComplete(any()); + } + + private void callService() { + remoteService.writeFileContent(OzgFileTestFactory.ID, out); + } + } + + @Nested + class TestWaitUntilFutureToComplete { + + @Mock + private CompletableFuture<Boolean> streamFuture; + + @Test + void shouldNotThrowException() { + assertDoesNotThrow(() -> remoteService.waitUntilFutureToComplete(streamFuture)); + } + + @ParameterizedTest + @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) + void shouldRethrowAsTechnicalException(Class<Exception> exception) + throws InterruptedException, ExecutionException, TimeoutException { + doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); + + assertThrows(TechnicalException.class, () -> remoteService.waitUntilFutureToComplete(streamFuture)); + } + } + + @Nested + class TestGetFiles { + + private final GrpcFindFilesResponse response = BinaryFileTestFactory.createFindBinaryFilesResponse(); + + @BeforeEach + void mock() { + when(serviceStub.findBinaryFilesMetaData(any())).thenReturn(response); + when(fileIdMapper.toString(any())).thenReturn(OzgFileTestFactory.ID.toString()); + } + + @Test + void shouldCallContextService() { + doServiceCall(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallFileIdMapper() { + doServiceCall(); + + verify(fileIdMapper).toString(any()); + } + + @Test + void shouldCallOzgFileMapper() { + var result = doServiceCall(); + + result = result.collect(Collectors.toList()).stream(); + + verify(ozgFileMapper).toFile(any(GrpcOzgFile.class)); + } + + private Stream<OzgFile> doServiceCall() { + return remoteService.getFiles(List.of(OzgFileTestFactory.ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7f86bff09c0b554a807bc0c936ae7b5ae9d46e9c --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileServiceTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.file.OzgFileTestFactory; + +class BinaryFileServiceTest { + + @InjectMocks + private BinaryFileService service; + + @Mock + private BinaryFileRemoteService remoteService; + + @Nested + class TestUploadFile { + + @Test + void shouldCallRemoteService() throws IOException { + var uploadBinaryFile = UploadBinaryFileTestFactory.create(); + + service.uploadFile(uploadBinaryFile); + + verify(remoteService).uploadFile(uploadBinaryFile); + } + } + + @Nested + class TestWriteFileContent { + + @Mock + private OutputStream out; + + @Test + void shouldCallRemotService() { + service.writeFileContent(OzgFileTestFactory.ID, out); + + verify(remoteService).writeFileContent(OzgFileTestFactory.ID, out); + } + } + + @Nested + class TestGetFile { + + @Test + void shouldCallRemoteService() { + service.getFile(BinaryFileTestFactory.FILE_ID); + + verify(remoteService).getFile(BinaryFileTestFactory.FILE_ID); + } + } + + @Nested + class TestGetFiles { + + @Test + void shouldCallRemotService() { + var fileIds = List.of(BinaryFileTestFactory.FILE_ID); + + service.getFiles(fileIds); + + verify(remoteService).getFiles(fileIds); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e010d83395fab88df4d8e62d5dbe38f8b66bd2cf --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileTestFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.UUID; + +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.util.unit.DataSize; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.file.GrpcOzgFileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcFindFilesResponse; + +public class BinaryFileTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final FileId FILE_ID = FileId.from(ID); + public static final String NAME = "TestFile"; + public static final String CONTENT_TYPE = "application/image"; + public static final byte[] DATA = "juhu, ein bild".getBytes(); + public static final MockMultipartFile TEST_FILE = new MockMultipartFile("file", NAME, CONTENT_TYPE, DATA); + public static final long SIZE = TEST_FILE.getSize(); + + public static final String FIELD = "attachment"; + public static final String VORGANG_ID = VorgangHeaderTestFactory.ID; + static final InputStream STREAM = new ByteArrayInputStream(DATA); + + public static GrpcFindFilesResponse createFindBinaryFilesResponse() { + return GrpcFindFilesResponse.newBuilder().addFile(GrpcOzgFileTestFactory.create()).build(); + } + + public static byte[] createByteOfSize(DataSize size) { + var testStr = LoremIpsum.getInstance().getWords(1); + var contentSize = (int) size.toBytes(); + var result = new byte[contentSize]; + + System.arraycopy(testStr.getBytes(), 0, result, contentSize - testStr.length(), testStr.length()); + + return result; + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserverTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserverTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fcfa98ab907667328230d38ce53ec91b5cad8ad1 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/BinaryFileUploadStreamObserverTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.util.ReflectionTestUtils; + +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileResponse; + +class BinaryFileUploadStreamObserverTest { + + @InjectMocks + private BinaryFileUploadStreamObserver uploadStreamObserver; + @Mock + private CompletableFuture<FileId> fileIdFuture; + + private final String FIELD_FILE_ID = "fileId"; + + @Nested + class TestOnNext { + + private GrpcUploadBinaryFileResponse uploadResponse = GrpcUploadBinaryFileResponse.newBuilder() + .setFileId(OzgFileTestFactory.ID.toString()) + .build(); + + @Test + void shouldSetFileId() { + uploadStreamObserver.onNext(uploadResponse); + + assertThat(uploadStreamObserver.getFileId()).isEqualTo(OzgFileTestFactory.ID.toString()); + } + } + + @Nested + class TestOnError { + + @Mock + private Throwable throwable; + + @Test + void shouldCompleteFutureExceptionally() { + uploadStreamObserver.onError(throwable); + + verify(fileIdFuture).completeExceptionally(throwable); + } + } + + @Nested + class TestOnCompleted { + + @Test + void shouldCompleteWithFileId() { + ReflectionTestUtils.setField(uploadStreamObserver, FIELD_FILE_ID, BinaryFileTestFactory.ID); + + uploadStreamObserver.onCompleted(); + + verify(fileIdFuture).complete(FileId.from(BinaryFileTestFactory.ID)); + } + + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSenderTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSenderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8a1a3fff1fe4c19b7635e3693198c55005d2eba4 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/ChunkedFileSenderTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest; +import io.grpc.stub.CallStreamObserver; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +class ChunkedFileSenderTest { + + private CallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver = mock(CallStreamObserver.class); + private InputStream uploadStream = mock(InputStream.class); + + private int chunkSize = BinaryFileTestFactory.DATA.length; + private GrpcUploadBinaryFileRequest uploadBinaryFileRequest = GrpcUploadBinaryFileRequestTestFactory.createDataRequest(); + private Function<byte[], GrpcUploadBinaryFileRequest> buildUploadBinaryFileRequest = when(mock(Function.class).apply(any())).thenReturn(uploadBinaryFileRequest).getMock(); + private GrpcUploadBinaryFileRequest metadataRequest = GrpcUploadBinaryFileRequestTestFactory.createMetadataRequest(); + + private ChunkedFileSender<GrpcUploadBinaryFileRequest> streamer = new ChunkedFileSender<>(uploadStream, chunkSize, buildUploadBinaryFileRequest, metadataRequest); + + @Nested + class TestSendBinaryFile { + + @Test + void shouldNotSendWhenDone() { + ReflectionTestUtils.setField(streamer, "hasUploadFile", new AtomicBoolean(false)); + + streamer.sendChunkTo(requestObserver); + + verify(requestObserver, never()).onNext(any()); + } + + @SneakyThrows + @Test + void shouldCloseUploadStream() { + when(uploadStream.readNBytes(anyInt())).thenReturn(new byte[]{}); + + streamer.sendChunkTo(requestObserver); + + verify(uploadStream).close(); + } + + @SneakyThrows + @Test + void shouldCallOnCompleted() { + when(uploadStream.readNBytes(anyInt())).thenReturn(new byte[]{}); + + streamer.sendChunkTo(requestObserver); + + verify(requestObserver).onCompleted(); + } + + @SneakyThrows + @Test + void shouldCallOnNext(){ + when(uploadStream.readNBytes(anyInt())).thenReturn(BinaryFileTestFactory.DATA); + + streamer.sendChunkTo(requestObserver); + + verify(requestObserver).onNext(uploadBinaryFileRequest); + } + + @SneakyThrows + @Test + void shouldThrowException() { + doThrow(IOException.class).when(uploadStream).readNBytes(anyInt()); + + assertThrows(TechnicalException.class, () -> streamer.sendChunkTo(requestObserver)); + } + + @SneakyThrows + @Test + void shouldBuildRequest() { + when(uploadStream.readNBytes(anyInt())).thenReturn(BinaryFileTestFactory.DATA); + + streamer.sendChunkTo(requestObserver); + + verify(buildUploadBinaryFileRequest).apply(BinaryFileTestFactory.DATA); + } + + @SneakyThrows + @Test + void shouldSendMetadata() { + when(uploadStream.readNBytes(anyInt())).thenReturn(BinaryFileTestFactory.DATA); + + streamer.sendChunkTo(requestObserver); + + verify(requestObserver).onNext(metadataRequest); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandlerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ee359807deaedacde56a394118c8dd3d312b7e36 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadAuthenticationHandlerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; + +class DownloadAuthenticationHandlerTest { + + private DownloadAuthenticationHandler downloadAuthorizationHandler = new DownloadAuthenticationHandler(); + + @Nested + class TestAuthorizationKeycloak { + + private Authentication authentication = mock(Authentication.class); + + @BeforeEach + void init() { + when(authentication.isAuthenticated()).thenReturn(Boolean.TRUE); + KeycloakAuthenticationToken keycloakToken = mock(KeycloakAuthenticationToken.class); + when(authentication.getPrincipal()).thenReturn(keycloakToken); + } + + @Test + void shouldAuthenticate() { + assertThat(downloadAuthorizationHandler.check(FileId.createNew(), authentication)).isTrue(); + } + } + + @Nested + class TestAuthorizationUsernamePassword { + private FileId fileId = FileId.createNew(); + + private Authentication authentication = mock(UsernamePasswordAuthenticationToken.class); + + @BeforeEach + void init() { + when(authentication.isAuthenticated()).thenReturn(Boolean.TRUE); + when(authentication.getPrincipal()).thenReturn(DownloadGoofyUserTestFactory.createBuilder().fileId(fileId).build()); + } + + @Test + void shouldAuthenticate() { + assertThat(downloadAuthorizationHandler.check(fileId, authentication)).isTrue(); + } + + @Test + void shouldNotAuthenticateWrongFileId() { + assertThat(downloadAuthorizationHandler.check(FileId.createNew(), authentication)).isFalse(); + } + + @Test + void shouldNotAuthenticateNoFileId() { + assertThat(downloadAuthorizationHandler.check(null, authentication)).isFalse(); + } + + @Test + void shouldNotAuthenticate() { + when(authentication.isAuthenticated()).thenReturn(Boolean.FALSE); + assertThat(downloadAuthorizationHandler.check(fileId, authentication)).isFalse(); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadGoofyUserTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadGoofyUserTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..5b13b74510dbd84f70686b683133b99af7105f5d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/DownloadGoofyUserTestFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class DownloadGoofyUserTestFactory { + + static GoofyUserWithFileId create() { + return createBuilder().build(); + } + + static GoofyUserWithFileId.GoofyUserWithFileIdBuilder createBuilder() { + return GoofyUserWithFileId.builder().user(UserProfileTestFactory.create()).fileId(BinaryFileTestFactory.FILE_ID); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileMetaDataTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileMetaDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3b275ecb36ca747698727d2d7a5baa8c75b8262b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileMetaDataTestFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileMetaData; + +public class GrpcUploadBinaryFileMetaDataTestFactory { + + public static GrpcUploadBinaryFileMetaData create() { + return createBuilder().build(); + } + + private static GrpcUploadBinaryFileMetaData.Builder createBuilder() { + return GrpcUploadBinaryFileMetaData.newBuilder() + .setVorgangId(UploadBinaryFileTestFactory.VORGANG_ID) + .setField(UploadBinaryFileTestFactory.FIELD) + .setContentType(OzgFileTestFactory.CONTENT_TYPE) + .setSize(OzgFileTestFactory.SIZE) + .setFileName(OzgFileTestFactory.NAME); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileRequestTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..fc694520bfb47563cb4e459e9106c450f936556e --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/GrpcUploadBinaryFileRequestTestFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import com.google.protobuf.ByteString; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest; +import de.itvsh.ozg.pluto.grpc.binaryFile.GrpcUploadBinaryFileRequest.Builder; + +public class GrpcUploadBinaryFileRequestTestFactory { + + public static GrpcUploadBinaryFileRequest createDataRequest() { + return createBuilder().setFileContent(ByteString.copyFrom(BinaryFileTestFactory.DATA)).build(); + } + + public static GrpcUploadBinaryFileRequest createMetadataRequest() { + return createBuilder().setMetadata(GrpcUploadBinaryFileMetaDataTestFactory.create()).build(); + } + + public static Builder createBuilder() { + return GrpcUploadBinaryFileRequest.newBuilder(); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..753bb47fac1f59cbf04b8f43f542ca59bcee2dcf --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileSizeValidatorTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.Map; + +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.util.unit.DataSize; + +class UploadBinaryFileSizeValidatorTest { + + @Spy + @InjectMocks + private UploadBinaryFileSizeValidator validator = new UploadBinaryFileSizeValidator(); + @Mock + private BinaryFileProperties properties; + + @Nested + class TestIsValid { + + @Mock + private ConstraintValidatorContext context; + @Mock + private HibernateConstraintValidatorContext unwrappedContext; + + @BeforeEach + void mockMaxValidSize() { + doReturn(unwrappedContext).when(validator).getUnwrappedContext(any(), any()); + doReturn(DataSize.ofMegabytes(3)).when(validator).getMaxValidSize(anyString()); + } + + @Test + void shouldCallGetMaxValidSize() { + validator.isValid(UploadBinaryFileTestFactory.create(), context); + + verify(validator).getMaxValidSize(UploadBinaryFileTestFactory.FIELD); + } + + @Test + void shouldReturnTrue() { + var result = validator.isValid(UploadBinaryFileTestFactory.create(), context); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnFalse() { + var result = validator.isValid(UploadBinaryFileTestFactory.createBuilder().size(DataSize.ofMegabytes(3).toBytes() + 1).build(), null); + + assertThat(result).isFalse(); + } + + @Nested + class TestDynamicPayload { + + @Captor + private ArgumentCaptor<DynamicViolationParameter> captor; + + @Test + void shouldSetMaxValue() { + validator.isValid(UploadBinaryFileTestFactory.create(), context); + + verify(unwrappedContext).withDynamicPayload(captor.capture()); + assertThat(String.valueOf(captor.getValue().getMap().get("max"))).isEqualTo("3"); + } + } + } + + @Nested + class TestGetMaxValidSize { + + @Test + void shouldReturnDefaultValueOnEmptyMap() { + when(properties.getMaxFileSize()).thenReturn(Collections.emptyMap()); + + var maxFileSize = validator.getMaxValidSize(BinaryFileTestFactory.FIELD); + + assertThat(maxFileSize.toBytes()).isEqualTo(DataSize.ofMegabytes(UploadBinaryFileSizeValidator.DEFAULT_MAX_SIZE).toBytes()); + } + + @Test + void shouldReturnDefaultValueOnMissingField() { + when(properties.getMaxFileSize()).thenReturn(Map.of("not match", DataSize.ofMegabytes(3))); + + var maxFileSize = validator.getMaxValidSize(BinaryFileTestFactory.FIELD); + + assertThat(maxFileSize.toBytes()).isEqualTo(DataSize.ofMegabytes(UploadBinaryFileSizeValidator.DEFAULT_MAX_SIZE).toBytes()); + } + + @Test + void shouldReturnPropertiesValue() { + when(properties.getMaxFileSize()).thenReturn(Map.of(BinaryFileTestFactory.FIELD, DataSize.ofMegabytes(3))); + + var maxFileSize = validator.getMaxValidSize(BinaryFileTestFactory.FIELD); + + assertThat(maxFileSize.toBytes()).isEqualTo(DataSize.ofMegabytes(3).toBytes()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d4347a210fde373571bc3c4d20e223b444239f82 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/binaryfile/UploadBinaryFileTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.binaryfile; + +import static de.itvsh.goofy.common.file.OzgFileTestFactory.*; + +import java.io.InputStream; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +public class UploadBinaryFileTestFactory { + + static final String VORGANG_ID = VorgangHeaderTestFactory.ID; + static final String FIELD = BinaryFileTestFactory.FIELD; + static final InputStream UPLOAD_STREAM = BinaryFileTestFactory.STREAM; + + public static UploadBinaryFileRequest create() { + return createBuilder().build(); + } + + public static UploadBinaryFileRequest.UploadBinaryFileRequestBuilder createBuilder() { + return UploadBinaryFileRequest.builder() + .name(NAME) + .vorgangId(VORGANG_ID) + .field(FIELD) + .size(SIZE) + .contentType(CONTENT_TYPE) + .uploadStream(UPLOAD_STREAM); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e87a2de0026eefec0c2d7dbbbf4f33aa026c2538 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextAttachingInterceptorTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.callcontext.CallContextAttachingInterceptor.CallContextAttachingClientCall; +import io.grpc.ClientCall; +import io.grpc.ClientCall.Listener; +import io.grpc.Metadata; + +class CallContextAttachingInterceptorTest<A, B> { + + @InjectMocks + private CallContextAttachingInterceptor interceptor; + @Mock + private ContextService contextService; + + private CallContextAttachingClientCall<A, B> clientCall; + @Mock + private ClientCall<A, B> delegateCall; + + @Mock + private Listener<B> responseListener; + + @Mock + private Metadata contextMetadata; + @Mock + private Metadata inputMetadata; + + @BeforeEach + void initClientCall() { + clientCall = interceptor.new CallContextAttachingClientCall<A, B>(delegateCall); + } + + @BeforeEach + void mockContextService() { + when(contextService.buildCallContextMetadata()).thenReturn(contextMetadata); + } + + @Test + void shouldCallUserService() { + clientCall.start(responseListener, inputMetadata); + + verify(contextService).buildCallContextMetadata(); + } + + @Test + void shouldMergeMetas() { + clientCall.start(responseListener, inputMetadata); + + verify(inputMetadata).merge(contextMetadata); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c20bb0a856114a34c80f9d615b1ee923a971b471 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/CallContextTestFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import static de.itvsh.goofy.common.callcontext.ContextService.*; + +import java.util.Map; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class CallContextTestFactory { + + public static final String CLIENT_NAME = "TEST_CLIENT"; + + static Map<String, String> createContextMap() { + return Map.of( + KEY_USER_ID, UserProfileTestFactory.ID.toString(), + KEY_USER_NAME, UserProfileTestFactory.FULLNAME); + + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/ContextServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/ContextServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b31cfa1726d4be392982c17639c2ac5fdce5a1fa --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/callcontext/ContextServiceTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.callcontext; + +import static de.itvsh.goofy.common.callcontext.ContextService.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collection; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.context.ApplicationContext; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.RequestAttributes; +import de.itvsh.goofy.RequestAttributesTestFactory; +import de.itvsh.goofy.common.GrpcUtil; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.ZustaendigeStelleTestFactory; +import de.itvsh.ozg.pluto.grpc.command.GrpcUser; + +class ContextServiceTest { + + static final String APPLICATION_ID = LoremIpsum.getInstance().getName(); + + @InjectMocks + private ContextService service; + + @Mock + private CurrentUserService userService; + @Mock + private ApplicationContext context; + @Spy + private RequestAttributes reqAttributes = RequestAttributesTestFactory.create(); + + @BeforeEach + void initMocks() { + when(context.getId()).thenReturn(APPLICATION_ID); + when(userService.getUser()).thenReturn(UserProfileTestFactory.create()); + } + + @DisplayName("Get context metas") + @Nested + class TestGetContextMetas { + + @BeforeEach + void mockUserService() { + when(userService.hasRole(any())).thenReturn(Boolean.TRUE); + } + + @Test + void shouldHaveUserId() { + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_USER_ID, metadata)).isEqualTo(UserProfileTestFactory.ID.toString()); + } + + @Test + void shouldHaveUserName() { + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_USER_NAME, metadata)).isEqualTo(UserProfileTestFactory.FULLNAME); + } + + @Test + void shouldHaveClientName() { + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_CLIENT_NAME, metadata)).isEqualTo(APPLICATION_ID); + } + + @Test + void shouldLimitAccessToOrgaId() { + var metadata = service.buildCallContextMetadata(); + metadata.put(GrpcUtil.createKeyOf(KEY_ACCESS_LIMITED_ORGAID), "orgaid_2".getBytes()); + + assertThat(GrpcUtil.getCollection(KEY_ACCESS_LIMITED_ORGAID, metadata)).isInstanceOf(Collection.class).hasSize(2) + .contains(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID, "orgaid_2"); + } + + @DisplayName("access limited") + @Nested + class TestAccessLimitedByUserRole { + + @Test + void shouldHaveTrueOnRoleVerwaltungUser() { + when(userService.hasRole(anyString())).thenReturn(Boolean.FALSE); + + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_ACCESS_LIMITED, metadata)).isEqualTo(Boolean.TRUE.toString()); + } + + @Test + void shouldHaveFalseOnRoleVerwaltungPoststelle() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(Boolean.TRUE); + + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_ACCESS_LIMITED, metadata)).isEqualTo(Boolean.FALSE.toString()); + } + + @Test + void shouldHaveFalseOnRoleEinheitlicherAnsprechpartner() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(Boolean.FALSE); + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(Boolean.TRUE); + + var metadata = service.buildCallContextMetadata(); + + assertThat(GrpcUtil.getFromHeaders(KEY_ACCESS_LIMITED, metadata)).isEqualTo(Boolean.FALSE.toString()); + } + } + } + + @Nested + class TestCreateCallContest { + + @Test + void shouldCallContextGetApplicationName() { + service.createCallContext(); + + verify(context).getId(); + } + + @Test + void shouldContainClient() { + var callContext = service.createCallContext(); + + assertThat(callContext.getClient()).isEqualTo(APPLICATION_ID); + } + + @Test + void shouldCallUserService() { + service.createCallContext(); + + verify(userService).getUser(); + } + + @Test + void shoultHaveUserId() { + var context = service.createCallContext(); + + assertThat(context.getUser()).extracting(GrpcUser::getId).isEqualTo(UserProfileTestFactory.ID.toString()); + } + + @Test + void shouldHaveFullName() { + var context = service.createCallContext(); + + assertThat(context.getUser()).extracting(GrpcUser::getName).isEqualTo(UserProfileTestFactory.FULLNAME); + } + + @Test + void shouldHaveRequestId() { + var context = service.createCallContext(); + + assertThat(context.getRequestId()).isEqualTo(RequestAttributesTestFactory.REQUEST_ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7aac3f5263273ea80b1d231ff45208f8b43b452d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeRemoteServiceTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.clientattribute; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.ozg.pluto.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub; + +class ClientAttributeRemoteServiceTest { + @Nested + class TestResetNewPostfachNachricht { + @Spy + @InjectMocks + private ClientAttributeRemoteService service; + + @Mock + private ClientAttributeServiceBlockingStub serviceStub; + + @Test + void shouldCallServiceStub() { + service.resetPostfachNachricht(any()); + + verify(serviceStub).update(any()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a3a6c9863c4be0b9a726e38da96731e0fa3cddb3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/ClientAttributeServiceTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.clientattribute; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcUpdateClientAttributeRequest; + +class ClientAttributeServiceTest { + @Spy + @InjectMocks + private ClientAttributeService service = new ClientAttributeService(); + @Mock + private ClientAttributeRemoteService remoteService; + + @Nested + class TestResetNewPostfachNachricht { + @Test + void shouldCallRemoteService() { + service.resetPostfachNachricht(VorgangHeaderTestFactory.ID); + + verify(remoteService).resetPostfachNachricht(any()); + } + } + + @Nested + class BuildRequest { + + @Test + void shouldHaveAttribute() { + GrpcUpdateClientAttributeRequest request = service.buildResetNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + assertThat(request.getAttribute()).isNotNull(); + } + + @Test + void shouldHaveVorgangId() { + GrpcUpdateClientAttributeRequest request = service.buildResetNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + assertThat(request.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldHaveAttributeName() { + GrpcUpdateClientAttributeRequest request = service.buildResetNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + assertThat(request.getAttribute().getAttributeName()).isEqualTo(ClientAttributeService.HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME); + } + + @Test + void shouldHaveClientName() { + GrpcUpdateClientAttributeRequest request = service.buildResetNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + assertThat(request.getAttribute().getClientName()).isEqualTo(ClientAttributeService.ORIGINAL_CLIENT_NAME); + } + + @Test + void shouldHaveAttributeValue() { + GrpcUpdateClientAttributeRequest request = service.buildResetNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + assertThat(request.getAttribute().getValue()).isNotNull(); + assertThat(request.getAttribute().getValue().getBoolValue()).isFalse(); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/GrpcClientAttributeTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/GrpcClientAttributeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..497731f92e6139c215017fa663143fd131475ca4 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/clientattribute/GrpcClientAttributeTestFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.clientattribute; + +import de.itvsh.goofy.common.callcontext.CallContextTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcAccessPermission; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; + +public class GrpcClientAttributeTestFactory { + static final String CLIENT_NAME = CallContextTestFactory.CLIENT_NAME; + static final GrpcAccessPermission ACCESS_PERMISSION = GrpcAccessPermission.READ_ONLY; + static final GrpcClientAttributeValue ATTRIBUTE_VALUE = GrpcClientAttributeValue.newBuilder().setBoolValue(true).build(); + + public static final String ATTRIBUTE_NAME = "testAttribute"; + + public static GrpcClientAttribute create() { + return createBuilder().build(); + } + + public static GrpcClientAttribute.Builder createBuilder() { + return GrpcClientAttribute.newBuilder() + .setAccess(ACCESS_PERMISSION) + .setClientName(CLIENT_NAME) + .setAttributeName(ATTRIBUTE_NAME) + .setValue(ATTRIBUTE_VALUE); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandBodyMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandBodyMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a16401ff88be9690e84bbf17efcc0f31530471ef --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandBodyMapperTest.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.postfach.PostfachMailTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.wiedervorlage.WiedervorlageTestFactory; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandBody; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandBodyField; + +class CommandBodyMapperTest { + + private static final String FIELD = "FIELD"; + private static final String VALUE = "VALUE"; + + @Spy + private CommandBodyMapper mapper = Mappers.getMapper(CommandBodyMapper.class); + + @DisplayName("Map from object to map") + @Nested + class TestFromObjectToMap { + + @Test + void shouldMapAllValues() { + var commandAsObject = CommandTestFactory.create(); + + var mappedMap = mapper.fromObjectToMap(commandAsObject); + + assertThat(mappedMap).hasSize(12) + .containsEntry("id", CommandTestFactory.ID) + .containsEntry("status", CommandTestFactory.STATUS.name()) + .containsEntry("order", CommandTestFactory.ORDER.name()) + .containsEntry("relationId", CommandTestFactory.RELATION_ID) + .containsEntry("vorgangId", CommandTestFactory.VORGANG_ID); + } + + @Test + void shouldNotMapClass() { + var commandAsObject = CommandTestFactory.create(); + + var mappedMap = mapper.fromObjectToMap(commandAsObject); + + assertThat(mappedMap).doesNotContainKey("class"); + } + + @Test + void shouldMapNullValue() { + var commandAsObject = CommandTestFactory.createBuilder().status(null).build(); + + var mappedMap = mapper.fromObjectToMap(commandAsObject); + + assertThat(mappedMap).containsEntry("status", null); + } + + @Test + void shouldNotMapVersion() { + var commandAsObject = WiedervorlageTestFactory.create(); + + var mappedMap = mapper.fromObjectToMap(commandAsObject); + + assertThat(mappedMap).doesNotContainKey("version"); + } + } + + @Nested + class TestMapToBody { + @Test + void shouldReturnEmptyBodyForNull() { + var result = mapper.mapToBody(null); + + assertThat(result).isNotNull(); + assertThat(result.getFieldList()).isEmpty(); + } + + @Test + void shouldReturnBodyWithField() { + var result = mapper.mapToBody(buildBodyMap()); + + assertThat(result.getFieldList()).hasSize(1); + assertThat(result.getFieldList().get(0).getName()).isEqualTo("FIELD"); + assertThat(result.getFieldList().get(0).getValue()).isEqualTo("VALUE"); + } + + private Map<String, String> buildBodyMap() { + return Map.of(FIELD, VALUE); + } + + @Test + void shouldFilterEntriesWithNullValue() { + var result = mapper.mapToBody(buildBodyMapWithNullValue()); + + assertThat(result.getFieldList()).isEmpty(); + } + + private Map<String, String> buildBodyMapWithNullValue() { + HashMap<String, String> bodyMap = new HashMap<>(); + bodyMap.put(FIELD, null); + return bodyMap; + } + } + + @Nested + class TestMapToBodyFields { + + @Captor + private ArgumentCaptor<Map<Object, Object>> mapCaptor; + + @Test + void shouldReturnEmptyListOnNull() { + var result = mapper.mapToBodyFields((CommandBody) null); + + assertThat(result).isEmpty(); + } + + @Test + void shouldContainFields() { + var fieldList = mapper.mapToBodyFields(PostfachMailTestFactory.create()); + + assertThat(fieldList).hasSize(13); + } + + @Test + void shouldNOTContainClassEntry() { + var fieldList = mapper.mapToBodyFields(PostfachMailTestFactory.create()); + + assertThat(fieldList).isNotEmpty().allMatch(field -> !StringUtils.equals(field.getName(), "class")); + } + + @Test + void shouldReturnField() { + var result = mapper.mapToBodyFields(buildBodyMap()); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getName()).isEqualTo(FIELD); + assertThat(result.get(0).getValue()).isEqualTo(VALUE); + } + + private Map<Object, Object> buildBodyMap() { + return Map.of(FIELD, VALUE); + } + + @Test + void shouldMapNullAsEmptyValue() { + var result = mapper.mapToBodyFields(buildBodyMapWithNullValue()); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getValue()).isEmpty(); + } + + private Map<Object, Object> buildBodyMapWithNullValue() { + HashMap<Object, Object> bodyMap = new HashMap<>(); + bodyMap.put(FIELD, null); + return bodyMap; + } + } + + @Nested + class TestMap { + + @Test + void shouldReturnMap() { + var result = mapper + .map(GrpcCommandBody.newBuilder().addField(GrpcCommandBodyField.newBuilder().setName(FIELD).setValue(VALUE).build()).build()); + + assertThat(result).hasSize(1).containsEntry(FIELD, VALUE); + } + } + + @Nested + class TestMapToBodyMap { + + @Nested + class ForWiedervorlage { + + private final CommandBody wiedervorlageCommandBody = WiedervorlageTestFactory.create(); + private final CreateCommand command = CommandTestFactory.createCreateCommandBuilder().body(wiedervorlageCommandBody).build(); + private final String itemName = "Wiedervorlage"; + + @Test + void shouldContainsItemName() { + var itemNameEntry = Map.entry(CommandBodyMapper.ITEM_NAME_PROPERTY, itemName); + + var mappedBody = mapToBodyMap(); + + assertThat(mappedBody).contains(itemNameEntry); + } + + @Test + void shouldContainsVorgangId() { + var vorgangIdEntry = Map.entry(CommandBodyMapper.VORGANG_ID_PROPERTY, VorgangHeaderTestFactory.ID); + + var mappedBody = mapToBodyMap(); + + assertThat(mappedBody).contains(vorgangIdEntry); + } + + @Nested + class CreatedItem { + + @Test + void shouldContainsBetreff() { + var betreffEntry = Map.entry("betreff", WiedervorlageTestFactory.BETREFF); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(betreffEntry); + } + + @Test + void shouldContainsBeschreibung() { + var beschreibungEntry = Map.entry("beschreibung", WiedervorlageTestFactory.BESCHREIBUNG); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(beschreibungEntry); + } + + @Test + void shouldContainsFrist() { + var fristEntry = Map.entry("frist", WiedervorlageTestFactory.FRIST); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(fristEntry); + } + + @Test + void shouldContainsCreatedBy() { + var createdByEntry = Map.entry("createdBy", WiedervorlageTestFactory.CREATED_BY); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(createdByEntry); + } + + @Test + void shouldContainsCreatedAt() { + var createdAtEntry = Map.entry("createdAt", WiedervorlageTestFactory.CREATED_AT); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(createdAtEntry); + } + + @Test + void shouldContainsDone() { + var doneEntry = Map.entry("done", WiedervorlageTestFactory.DONE); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(doneEntry); + } + + @Test + void shouldContainsAttachments() { + var attachmentsEntry = Map.entry("attachments", List.of(BinaryFileTestFactory.FILE_ID)); + + var mappedItemMap = getMappedItemEntry(); + + assertThat(mappedItemMap).contains(attachmentsEntry); + } + + @SuppressWarnings("unchecked") + private Map<String, Object> getMappedItemEntry() { + return (Map<String, Object>) mapToBodyMap().get(CommandBodyMapper.ITEM_PROPERTY); + } + } + + private Map<String, Object> mapToBodyMap() { + return mapper.mapToBodyMap(command, itemName); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandByRelationControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandByRelationControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68b300976a7ce2545ae7457d3a1a4f1461848565 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandByRelationControllerTest.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static de.itvsh.goofy.common.command.CommandController.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.postfach.PostfachMail; +import de.itvsh.goofy.postfach.PostfachMailTestFactory; +import de.itvsh.goofy.vorgang.AntragstellerTestFactory; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +class CommandByRelationControllerTest { + + @Spy + @InjectMocks // NOSONAR + private CommandByRelationController controller; + @Mock + private CommandService service; + @Mock + private CommandModelAssembler modelAssembler; + @Mock + private VorgangController vorgangController; + + private MockMvc mockMvc; + + @BeforeEach + void initMockMvc() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + @DisplayName("Create Command by Vorgang") + class CreateCommandByVorgang { + + @Captor + private ArgumentCaptor<CreateCommand> createCommandCaptor; + + @BeforeEach + void initTest() { + when(service.createCommand(any(CreateCommand.class), anyLong())).thenReturn(CommandTestFactory.create()); + } + + @DisplayName("should return CREATED") + @Test + void returnCreated() throws Exception { + doRequest().andExpect(status().isCreated()); + } + + @DisplayName("should call service") + @Test + void callService() throws Exception { + doRequest(); + + verify(service).createCommand(createCommandCaptor.capture(), eq(CommandTestFactory.RELATION_VERSION)); + assertThat(createCommandCaptor.getValue().getOrder()).isEqualTo(CommandTestFactory.ORDER); + assertThat(createCommandCaptor.getValue().getVorgangId()).isEqualTo(CommandTestFactory.VORGANG_ID); + assertThat(createCommandCaptor.getValue().getRelationId()).isEqualTo(CommandTestFactory.RELATION_ID); + assertThat(createCommandCaptor.getValue().getRelationVersion()).isZero(); + } + + @DisplayName("should have location header") + @Test + void locationHeader() throws Exception { + doRequest() + .andExpect(header().stringValues("location", "http://localhost" + COMMANDS_PATH + "/" + CommandTestFactory.ID)); + } + + @Nested + @DisplayName("CreateCommand with postfach mail") + + class WithPostfachMail { + + @Test + void shouldPrepareForPostfachNachricht() throws Exception { + doReturn(CommandTestFactory.createCreateCommand()).when(controller).addPostfachIdToBody(any(), any()); + + doRequest(PostfachMailTestFactory.buildSendPostfachMailContent()); + + verify(controller).addPostfachIdToBody(createCommandCaptor.capture(), eq(VorgangHeaderTestFactory.ID)); + assertThat(((PostfachMail) createCommandCaptor.getValue().getBody()).getAttachments().get(0)) + .isEqualTo(BinaryFileTestFactory.FILE_ID); + } + } + + private ResultActions doRequest() throws Exception { + return doRequest( + TestUtils.loadTextFile("jsonTemplates/command/createVorgangCommand.json.tmpl", CommandOrder.VORGANG_ANNEHMEN.name())); + } + + private ResultActions doRequest(String content) throws Exception { + return mockMvc + .perform(post(CommandByRelationController.COMMAND_BY_RELATION_PATH, VorgangHeaderTestFactory.ID, + CommandTestFactory.RELATION_ID, VorgangHeaderTestFactory.VERSION) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(content)) + .andExpect(status().is2xxSuccessful()); + } + } + + @DisplayName("prepare command for postfach nachricht") + @Nested + class TestPrepareCommandForPostfachNachricht { + + @BeforeEach + void mockVorgangController() { + when(vorgangController.getVorgang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + } + + @Test + void shouldCallVorgangController() { + controller.addPostfachIdToBody(createPostfachCreateCommand(), VorgangHeaderTestFactory.ID); + + verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldAddPostfachId() { + var preparedCommand = controller.addPostfachIdToBody(createPostfachCreateCommand(), VorgangHeaderTestFactory.ID); + + assertThat(((PostfachMail) preparedCommand.getBody()).getPostfachId()).isEqualTo(AntragstellerTestFactory.POSTFACH_ID); + } + + @Test + void shouldProceedWithEmptyAttachments() { + var createCommand = CommandTestFactory.createCreateCommandBuilder() + .body(PostfachMailTestFactory.createBuilder().clearAttachments().build()).build(); + + var preparedCommand = controller.addPostfachIdToBody(createCommand, VorgangHeaderTestFactory.ID); + + assertThat(((PostfachMail) preparedCommand.getBody()).getAttachments()).isEmpty(); + } + + private CreateCommand createPostfachCreateCommand() { + return CommandTestFactory.createCreateCommandBuilder().body(PostfachMailTestFactory.create()).build(); + } + } + + @DisplayName("Create by vorgang with relation id") + @Nested + class TestCreateByVorgang { + + private final CreateCommand command = CommandTestFactory.createCreateCommand(); + + @Test + void shouldCallSerice() { + controller.createCommand(command); + + verify(service).createCommand(command); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9b86f6b939c2fc3f91e25ef4b1b8765350ee63a8 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandControllerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static de.itvsh.goofy.common.command.CommandController.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.errorhandling.ExceptionController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +class CommandControllerTest { + + @InjectMocks // NOSONAR + private CommandController controller; + @Mock + private CommandService service; + @Mock + private CommandModelAssembler modelAssembler; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).setControllerAdvice(new ExceptionController()).build(); + } + + @Nested + class TestGetCommand { + + private final Command command = CommandTestFactory.create(); + + @BeforeEach + void initTest() { + when(service.getById(anyString())).thenReturn(command); + } + + @Test + void shouldReturnOk() throws Exception { + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).getById(CommandTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + doRequest(); + + verify(modelAssembler).toModel(command); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(CommandController.COMMANDS_PATH + "/" + CommandTestFactory.ID)).andExpect(status().is2xxSuccessful()); + } + } + + @Nested + @DisplayName("Revoke Command") + class TestRevokeCommand { + + @Test + void shouldReturnOk() throws Exception { + when(service.revoke(anyString())).thenReturn(CommandTestFactory.create()); + + doRequest(CommandStatus.REVOKED).andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + when(service.revoke(anyString())).thenReturn(CommandTestFactory.create()); + + doRequest(CommandStatus.REVOKED); + + verify(service).revoke(CommandTestFactory.ID); + } + + @Test + void shouldReturnForbiddenForOtherStatus() throws Exception { + doRequest(CommandStatus.FINISHED).andExpect(status().isForbidden()); + } + + private ResultActions doRequest(CommandStatus statusToPatch) throws Exception { + return mockMvc.perform(patch(COMMANDS_PATH + "/" + CommandTestFactory.ID).contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.loadTextFile("jsonTemplates/command/patchStatus.json", statusToPatch.name()))); + + } + } + + @Nested + class TestExistsPendingCommands { + + @Test + void shouldCallService() { + controller.existsPendingCommands(VorgangHeaderTestFactory.ID); + + verify(service).existsPendingCommands(VorgangHeaderTestFactory.ID); + } + } + + @Nested + class TestGetPendingCommands { + + private final Command command = CommandTestFactory.create(); + private final Stream<Command> pendingCommands = Stream.of(command); + + @BeforeEach + void initTest() { + when(service.getPendingCommands(anyString())).thenReturn(pendingCommands); + } + + @Test + void shouldReturnOk() throws Exception { + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).getPendingCommands(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + doRequest(); + + verify(modelAssembler).toCollectionModel(pendingCommands); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(CommandController.COMMANDS_PATH) + .param(CommandController.PARAM_PENDING, Boolean.toString(true)) + .param(CommandController.PARAM_VORGANG_ID, VorgangHeaderTestFactory.ID)) + .andExpect(status().is2xxSuccessful()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..402a9c75ecdfb984312fcfdbc4418cdaf35fa621 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandITCase.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.ValidationMessageCodes; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.postfach.PostfachMailTestFactory; +import de.itvsh.goofy.vorgang.RedirectRequestTestFactory; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import de.itvsh.kop.common.test.TestUtils; + +@AutoConfigureMockMvc +@SpringBootTest +public class CommandITCase { + + @Autowired + private MockMvc mockMvc; + @MockBean + private CommandRemoteService commandRemoteService; + @MockBean + private VorgangController vorgangController; + + @Nested + @WithMockUser + class TestAssignUser { + + @Captor + private ArgumentCaptor<CreateCommand> commandCaptor; + + @BeforeEach + void initTests() { + when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldExtractUserId() throws Exception { + createCommand(); + + verify(commandRemoteService).createCommand(commandCaptor.capture()); + assertThat(commandCaptor.getValue().getBody()).hasFieldOrPropertyWithValue("assignedTo", UserProfileTestFactory.ID); + } + + private void createCommand() throws Exception { + mockMvc.perform(post(buildUrl()).content(createContent()).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()); + } + + private String createContent() { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithBody.json.tmpl", CommandOrder.ASSIGN_USER.name(), + TestUtils.loadTextFile("jsonTemplates/command/commandAssignedToBody", + TestUtils.addQuote("/api/users/" + UserProfileTestFactory.ID.toString()))); + } + + private String buildUrl() { + return String.format("/api/vorgangs/%s/relations/%s/%d/commands", VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.ID, + VorgangHeaderTestFactory.VERSION); + } + } + + @DisplayName("Validation") + @Nested + class TestValidation { + + @DisplayName("of forwarding request") + @WithMockUser + @Nested + class TestRedirectRequest { + + @DisplayName("with null/empty email") + @WithMockUser + @Nested + class TestOnNullOrEmptyEmail { + + @Test + void shouldThrowErrorForEmailOnly() throws Exception { + var requestContent = buildRedirectRequest(RedirectRequestTestFactory.createBuilder() + .password(ArrayUtils.EMPTY_CHAR_ARRAY) + .email(null).build()); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.redirectRequest.email")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + } + + @DisplayName("with invalid email and password") + @WithMockUser + @Nested + class TestOnInvalidEmailAndPassword { + + @Test + void shouldThrowExceptionForEachOne() throws Exception { + var requestContent = buildRedirectRequest(RedirectRequestTestFactory.createBuilder() + .password(ArrayUtils.EMPTY_CHAR_ARRAY) + .email("local@@domain.com") + .build()); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(2)); + } + } + + @WithMockUser + @Nested + class TestEmail { + + private final String FIELD = "email"; + + @Test + void shouldReturnErrorOnNullEMail() throws Exception { + var requestContent = buildRedirectRequestWithEmail(null); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.redirectRequest." + FIELD)) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @Test + void shouldReturnErrorOnInvalidEMail() throws Exception { + var requestContent = buildRedirectRequestWithEmail("local@@domain.com"); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.redirectRequest." + FIELD)) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_INVALID)); + } + + private String buildRedirectRequestWithEmail(String eMail) { + return buildRedirectRequest(RedirectRequestTestFactory.createBuilder().email(eMail).build()); + } + } + + @WithMockUser + @Nested + class TestPassword { + + private final String FIELD = "password"; + + @Test + void shouldReturnErrorOnToShortPassword() throws Exception { + var requestContent = buildRedirectRequestWithPassword(RandomStringUtils.randomAlphabetic(7)); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.redirectRequest." + FIELD)) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_SIZE)) + .andExpect(jsonPath("$.issues.[0].parameters[0].name").value("min")) + .andExpect(jsonPath("$.issues.[0].parameters[0].value").value(8)) + .andExpect(jsonPath("$.issues.[0].parameters[1].name").value("max")) + .andExpect(jsonPath("$.issues.[0].parameters[1].value").value(40)); + } + + @Test + void shouldReturnErrorOnToLongPassword() throws Exception { + var requestContent = buildRedirectRequestWithPassword(RandomStringUtils.randomAlphabetic(41)); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.redirectRequest." + FIELD)) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_SIZE)); + } + + private String buildRedirectRequestWithPassword(String password) { + return buildRedirectRequest(RedirectRequestTestFactory.createBuilder().password(password.toCharArray()).build()); + } + } + + private String buildRedirectRequest(RedirectRequest request) { + return RedirectRequestTestFactory + .createRedirectRequestContent(CommandTestFactory.createCreateCommandBuilder().redirectRequest(request).build()); + } + } + + @WithMockUser + @Nested + class TestPostfachMail { + + @BeforeEach + void mockVorgangController() { + when(vorgangController.getVorgang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + } + + @WithMockUser + @Nested + class TestSubject { + + @BeforeEach + void initMockCommand() { + when(commandRemoteService.createCommand(any(CreateCommand.class))).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldReturnErrorOnNullSubject() throws Exception { + var requestContent = PostfachMailTestFactory + .buildSendPostfachMailContent(PostfachMailTestFactory.createBuilder().subject(null).build()); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.body.subject")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @Test + void shouldReturnErrorOnTooLongSubject() throws Exception { + var requestContent = PostfachMailTestFactory + .buildSendPostfachMailContent( + PostfachMailTestFactory.createBuilder().subject(RandomStringUtils.randomAlphanumeric(71)).build()); + + doRequest(requestContent).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.body.subject")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_MAX_SIZE)); + } + + @Test + void shouldProceedOnValidSubject() throws Exception { + var requestContent = PostfachMailTestFactory.buildSendPostfachMailContent(); + + doRequest(requestContent).andExpect(status().isCreated()); + } + } + + @WithMockUser + @Nested + class TestMailBody { + + @Test + void shouldReturnErrorOnNull() throws Exception { + var request = PostfachMailTestFactory + .buildSendPostfachMailContent(PostfachMailTestFactory.createBuilder().mailBody(null).build()); + + doRequest(request).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("command.body.mailBody")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @Test + void shouldProceedOnValidInput() throws Exception { + when(commandRemoteService.createCommand(any(CreateCommand.class))).thenReturn(CommandTestFactory.create()); + + var content = PostfachMailTestFactory.buildSendPostfachMailContent(); + + doRequest(content).andExpect(status().isCreated()); + } + } + } + + ResultActions doRequest(String content) throws Exception { + return mockMvc.perform(post("/api/vorgangs/" + CommandTestFactory.VORGANG_ID + "/relations/" + CommandTestFactory.RELATION_ID + "/" + + CommandTestFactory.RELATION_VERSION + "/commands") + .contentType(MediaType.APPLICATION_JSON) + .content(content)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d0bbf632db0b37356027d86174b49b21a4a56e31 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandMapperTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.TimeMapper; +import de.itvsh.goofy.common.callcontext.CallContextMapper; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.goofy.vorgang.RedirectRequestTestFactory; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.grpc.command.GrpcOrder; +import de.itvsh.ozg.pluto.grpc.command.GrpcRedirectRequest; + +class CommandMapperTest { + + @InjectMocks + private CommandMapper mapper = Mappers.getMapper(CommandMapper.class); + @Mock + private UserIdMapper userIdMapper; + @Mock + private GrpcObjectMapper grpcObjectMapper; + @Mock + private CommandBodyMapper bodyMapper; + @Mock + private TimeMapper timeMapper; + @Mock + private CallContextMapper callContextMapper; + + @Test + void shouldMapNonEnumOrder() { + var command = mapper.toCommand(GrpcCommandTestFactory.createBuilder() + .setOrder(GrpcOrder.UNDEFINED) + .setOrderString(CommandOrder.SEND_POSTFACH_NACHRICHT.name()) + .build()); + + assertThat(command.getOrder()).isEqualTo(CommandOrder.SEND_POSTFACH_NACHRICHT); + } + + @DisplayName("Map from grpc to command") + @Nested + class TestToCommand { + + @DisplayName("redirect request") + @Nested + class TestRedirectRequest { + + @Test + void shouldMapPasswordIfExists() { + var command = mapper.toCommand(GrpcCommandTestFactory.createBuilder() + .setRedirectRequest(GrpcRedirectRequest.newBuilder() + .setPassword(RedirectRequestTestFactory.PASSWORD_AS_STR) + .setEmail(RedirectRequestTestFactory.EMAIL) + .build()) + .build()); + + assertThat(command.getRedirectRequest().getPassword()).isEqualTo(RedirectRequestTestFactory.PASSWORD); + assertThat(command.getRedirectRequest().getEmail()).isEqualTo(RedirectRequestTestFactory.EMAIL); + } + + @Test + void shouldNotMapPasswordIfEmpty() { + var command = mapper.toCommand(GrpcCommandTestFactory.createBuilder() + .setRedirectRequest(GrpcRedirectRequest.newBuilder() + .setEmail(RedirectRequestTestFactory.EMAIL) + .build()) + .build()); + + assertThat(command.getRedirectRequest().getPassword()).isNull(); + assertThat(command.getRedirectRequest().getEmail()).isEqualTo(RedirectRequestTestFactory.EMAIL); + } + } + } + + @DisplayName("Map Body") + @Nested + class TestMapBody { + + private GrpcCommand command = GrpcCommandTestFactory.create(); + + @DisplayName("on existing body object at command") + @Nested + class TestOnExistingBodyObject { + + @Test + void shouldCallGrpcObjectMapper() { + mapper.mapBody(command); + + verify(grpcObjectMapper).mapFromGrpc(command.getBodyObj()); + } + + @Test + void shouldMapFromCommandBodyObject() { + var bodyObjectMap = Map.<String, Object>of("test-key", "test-value"); + when(grpcObjectMapper.mapFromGrpc(any())).thenReturn(bodyObjectMap); + + var result = mapper.mapBody(command); + + assertThat(result).isEqualTo(bodyObjectMap); + } + } + + @DisplayName("on empty body object at command") + @Nested + class TestOnEmptyBodyObject { + + private Map<String, String> mappedBody = Map.of("test-key", "test-value"); + + @BeforeEach + void mockMapper() { + when(grpcObjectMapper.mapFromGrpc(any())).thenReturn(Collections.emptyMap()); + when(bodyMapper.map(any())).thenReturn(mappedBody); + } + + @Test + void shouldCallBodyMapper() { + mapper.mapBody(command); + + verify(bodyMapper).map(command.getBody()); + } + + @Test + void shouldReturnMappedResult() { + var result = mapper.mapBody(command); + + assertThat(result).isEqualTo(mappedBody); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4d3fcff8f14e75f83b73bd1f48f0cacb857a5b24 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandModelAssemblerTest.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static de.itvsh.goofy.common.command.CommandModelAssembler.REL_EFFECTED_RESOURCE; +import static de.itvsh.goofy.common.command.CommandModelAssembler.REL_REVOKE; +import static de.itvsh.goofy.common.command.CommandModelAssembler.REL_UPDATE; +import static de.itvsh.goofy.common.command.CommandTestFactory.RELATION_ID; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.InjectMocks; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class CommandModelAssemblerTest { + + @InjectMocks // NOSONAR + private CommandModelAssembler modelAssembler; + + private final String COMMAND_SINGLE_PATH = "/api/commands/" + CommandTestFactory.ID; + + @Test + void shouldHaveSelfLink() { + var model = modelAssembler.toModel(CommandTestFactory.create()); + + assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); + } + + @Test + void shouldHaveRevokeLink() { + var model = modelAssembler.toModel(CommandTestFactory.create()); + + assertThat(model.getLink(CommandModelAssembler.REL_REVOKE)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); + } + + @Nested + @DisplayName("Link to effected Resource") + class TestEffectedResourceLink { + + @Nested + class TestOnVorgang { + + private final String VORGANG_URL = "/api/vorgangs/" + RELATION_ID; + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "FINISHED", "REVOKED" }) + void shouldBePresentOnStatus(CommandStatus status) { + var model = toModelWithStatus(status); + + assertThat(model.getLink(REL_EFFECTED_RESOURCE)).isPresent().get().extracting(Link::getHref).isEqualTo(VORGANG_URL); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "FINISHED", "REVOKED", "ERROR" }) + void shouldNOTbePresentOtherWise(CommandStatus status) { + var model = toModelWithStatus(status); + + assertThat(model.getLink(REL_EFFECTED_RESOURCE)).isEmpty(); + } + + private EntityModel<Command> toModelWithStatus(CommandStatus status) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().status(status).build()); + } + } + + @Nested + class TestOnWiedervorlage { + + private final String WIEDERVORLAGE_URL = "/api/wiedervorlages/" + RELATION_ID; + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_WIEDERVORLAGE", "EDIT_WIEDERVORLAGE", + "WIEDERVORLAGE_ERLEDIGEN", "WIEDERVORLAGE_WIEDEREROEFFNEN" }) + void shouldHaveLinkToWiedervorlage(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(CommandModelAssembler.REL_EFFECTED_RESOURCE)).isPresent().get().extracting(Link::getHref) + .isEqualTo(WIEDERVORLAGE_URL); + } + + private EntityModel<Command> toModelWithOrder(CommandOrder order) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); + } + } + + @Nested + class TestOnKommentar { + + private final String KOMMENTAR_URL = "/api/kommentars/" + CommandTestFactory.RELATION_ID; + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_KOMMENTAR", "EDIT_KOMMENTAR" }) + void shoulHaveLinkToKommentar(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(CommandModelAssembler.REL_EFFECTED_RESOURCE)).isPresent().get() + .extracting(Link::getHref).isEqualTo(KOMMENTAR_URL); + } + + private EntityModel<Command> toModelWithOrder(CommandOrder order) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); + } + } + + @Nested + class TestOnForwarding { + + private final String FORWARDING_URL = "/api/forwardings?vorgangId=" + VorgangHeaderTestFactory.ID; + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "FORWARD_SUCCESSFULL", "FORWARD_FAILED", "REDIRECT_VORGANG" }) + void shouldHaveLinkToForwarding(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(CommandModelAssembler.REL_EFFECTED_RESOURCE)).isPresent().get() + .extracting(Link::getHref).isEqualTo(FORWARDING_URL); + } + + private EntityModel<Command> toModelWithOrder(CommandOrder order) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); + } + } + + @Nested + class TestOnPostfach { + + private final String POSTFACH_URL = "/api/postfachMails?vorgangId=" + VorgangHeaderTestFactory.ID; + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "SEND_POSTFACH_MAIL" }) + void shouldHaveLinkToPostfach(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(CommandModelAssembler.REL_EFFECTED_RESOURCE)).isPresent().get() + .extracting(Link::getHref).isEqualTo(POSTFACH_URL); + } + + private EntityModel<Command> toModelWithOrder(CommandOrder order) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); + } + } + + @ParameterizedTest + @EnumSource + void shouldBeAbleToBuildLinkForEveryOrder(CommandOrder order) { + var link = modelAssembler.effectedResourceLinkByOrder(CommandTestFactory.createBuilder().order(order).build()); + + assertThat(link).isNotNull(); + } + } + + @Nested + class TestRevokeLink { + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE", + "WIEDERVORLAGE_WIEDEREROEFFNEN", "CREATE_KOMMENTAR", "EDIT_KOMMENTAR", "REDIRECT_VORGANG", "FORWARD_SUCCESSFULL", + "FORWARD_FAILED", "ASSIGN_USER", "SEND_POSTFACH_MAIL", "SEND_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL", "CREATE_ATTACHED_ITEM", + "UPDATE_ATTACHED_ITEM", "PATCH_ATTACHED_ITEM", "RECEIVE_POSTFACH_NACHRICHT" }) + void shouldHaveLink(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(REL_REVOKE)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); + } + + @ParameterizedTest + @EnumSource(names = { "CREATE_WIEDERVORLAGE", "WIEDERVORLAGE_ERLEDIGEN", "EDIT_WIEDERVORLAGE", + "WIEDERVORLAGE_WIEDEREROEFFNEN", "CREATE_KOMMENTAR", "EDIT_KOMMENTAR", "REDIRECT_VORGANG", "FORWARD_SUCCESSFULL" }) + void shouldNotHaveLink(CommandOrder order) { + var model = toModelWithOrder(order); + + assertThat(model.getLink(REL_REVOKE)).isNotPresent(); + } + + private EntityModel<Command> toModelWithOrder(CommandOrder order) { + return modelAssembler.toModel(CommandTestFactory.createBuilder().order(order).build()); + } + } + + @Nested + class TestUpdateLink { + + @ParameterizedTest + @EnumSource(names = { "PENDING", "REVOKE_PENDING" }) + void shouldHave(CommandStatus status) { + var model = modelAssembler.toModel(CommandTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(REL_UPDATE)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "PENDING", "REVOKE_PENDING" }) + void shouldNotHaveLink(CommandStatus status) { + var model = modelAssembler.toModel(CommandTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(REL_UPDATE)).isNotPresent(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b788a3d6b5c000463d107dd4e0a08f70838feda5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandRemoteServiceTest.java @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.vorgang.RedirectRequestTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.wiedervorlage.WiedervorlageTestFactory; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectTestFactory; +import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandResponse; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandsResponse; +import de.itvsh.ozg.pluto.grpc.command.GrpcCreateCommandRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcExistsPendingCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcExistsPendingCommandsResponse; +import de.itvsh.ozg.pluto.grpc.command.GrpcFindCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcGetPendingCommandsRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcGetPendingCommandsResponse; +import de.itvsh.ozg.pluto.grpc.command.GrpcRevokeCommandRequest; + +class CommandRemoteServiceTest { + + @Spy + @InjectMocks + private CommandRemoteService service; + @Mock + private CommandServiceBlockingStub commandServiceStub; + @Mock + private CommandMapper mapper; + @Mock + private CommandBodyMapper bodyMapper; + @Mock + private GrpcObjectMapper grpcObjectMapper; + @Mock + private ContextService contextService; + + private GrpcCallContext grpcCallContext = GrpcCallContext.newBuilder().build(); + + @Nested + @DisplayName("Create CreateCommand") + class TestCreateCommand { + + private GrpcCreateCommandRequest request = GrpcCreateCommandRequest.newBuilder().build(); + private CreateCommand createCommand = CommandTestFactory.createCreateCommand(); + + @Nested + class CallGrpcEndpoint { + + private Command resultCommand = CommandTestFactory.create(); + + @BeforeEach + void initTest() { + doReturn(request).when(service).buildCreateCommandRequest(any()); + when(commandServiceStub.createCommand(any())).thenReturn(GrpcCommandResponse.newBuilder().build()); + when(mapper.toCommand(any())).thenReturn(resultCommand); + } + + @Test + void shouldCreateRequest() { + service.createCommand(createCommand); + + verify(service).buildCreateCommandRequest(createCommand); + } + + @Test + void shouldCallServiceStub() { + service.createCommand(createCommand); + + verify(commandServiceStub).createCommand(request); + } + + @Test + void shouldReturnResultCommand() { + var result = service.createCommand(createCommand); + + assertThat(result).isSameAs(resultCommand); + } + } + + @DisplayName("build request") + @Nested + class TestBuildRequest { + + @BeforeEach + void initTest() { + when(contextService.createCallContext()).thenReturn(grpcCallContext); + when(grpcObjectMapper.fromMap(any())).thenReturn(GrpcObjectTestFactory.create()); + } + + @Test + void shouldCreateRequest() { + var request = buildRequest(); + + assertThat(request).isNotNull(); + } + + @Test + void shouldHaveOrder() { + var request = buildRequest(); + + assertThat(request.getOrderString()).isEqualTo(CommandTestFactory.ORDER.name()); + } + + @Test + void shouldHaveCallContext() { + var request = buildRequest(); + + assertThat(request.getCallContext()).isSameAs(grpcCallContext); + } + + @Test + void shouldHaveRelationId() { + var request = buildRequest(); + + assertThat(request.getRelationId()).isEqualTo(CommandTestFactory.RELATION_ID); + } + + @Test + void shouldHaveVorgangId() { + var request = buildRequest(); + + assertThat(request.getVorgangId()).isEqualTo(CommandTestFactory.VORGANG_ID); + } + + @Test + void shouldCallBodyMapper() { + buildRequest(); + + verify(bodyMapper).fromObjectToMap(any(CommandBody.class)); + } + + @Test + void shouldCallObjectMapper() { + buildRequest(); + + verify(grpcObjectMapper).fromMap(any()); + } + + @Test + void shouldHaveVersion() { + var request = buildRequest(); + + assertThat(request.getRelationVersion()).isEqualTo(VorgangHeaderTestFactory.VERSION); + } + + @Test + void shouldHaveRedirectRequest() { + var command = CommandTestFactory.createCreateCommandBuilder().body(null).redirectRequest(RedirectRequestTestFactory.create()).build(); + + var request = service.buildCreateCommandRequest(command); + + assertThat(request.getRedirectRequest().getPassword()).isEqualTo(RedirectRequestTestFactory.PASSWORD_AS_STR); + assertThat(request.getRedirectRequest().getEmail()).isEqualTo(RedirectRequestTestFactory.EMAIL); + } + + @ParameterizedTest + @EnumSource + void shouldHaveOrder(CommandOrder order) { + var request = service.buildCreateCommandRequest(CommandTestFactory.createCreateCommandBuilder().order(order).build()); + + assertThat(request.getOrderString()).isEqualTo(order.name()); + } + + private GrpcCreateCommandRequest buildRequest() { + return service.buildCreateCommandRequest( + CommandTestFactory.createCreateCommandBuilder().body(WiedervorlageTestFactory.create()).build()); + } + } + + @DisplayName("build forward request") + @Nested + class TestBuildForwardRequest { + + @Test + void shouldNotSetPassword() { + var request = RedirectRequestTestFactory.createBuilder().password(ArrayUtils.EMPTY_CHAR_ARRAY).build(); + + var result = service.buildForwardRequest(request); + + assertThat(result.getPassword()).isBlank(); + } + + @Test + void shouldSetPassword() { + var request = RedirectRequestTestFactory.createBuilder().password(RedirectRequestTestFactory.PASSWORD).build(); + + var result = service.buildForwardRequest(request); + + assertThat(result.getPassword()).isEqualTo(RedirectRequestTestFactory.PASSWORD_AS_STR); + } + + @Test + void shouldSetEmail() { + var request = RedirectRequestTestFactory.create(); + + var result = service.buildForwardRequest(request); + + assertThat(result.getEmail()).isEqualTo(RedirectRequestTestFactory.EMAIL); + } + } + } + + @Nested + @DisplayName("Revoke Command") + class TestRevokeCommand { + + @Nested + class CreateRequest { + + @BeforeEach + void initTest() { + when(contextService.createCallContext()).thenReturn(grpcCallContext); + } + + @Test + void shouldReturnRequest() { + var request = service.createRevokeRequest(CommandTestFactory.ID); + + assertThat(request).isNotNull().isInstanceOf(GrpcRevokeCommandRequest.class); + } + + @Test + void shouldHaveCallContext() { + var request = service.createRevokeRequest(CommandTestFactory.ID); + + assertThat(request.getContext()).isNotNull().isSameAs(grpcCallContext); + verify(contextService).createCallContext(); + } + + @Test + void shouldHaveCommandId() { + var request = service.createRevokeRequest(CommandTestFactory.ID); + + assertThat(request.getId()).isEqualTo(CommandTestFactory.ID); + } + } + + @Nested + class SendRequest { + + private GrpcRevokeCommandRequest request = GrpcRevokeCommandRequest.newBuilder().build(); + + @BeforeEach + void initTest() { + doReturn(request).when(service).createRevokeRequest(anyString()); + when(commandServiceStub.revokeCommand(any())).thenReturn(GrpcCommandResponse.newBuilder().build()); + } + + @Test + void shouldCreateRequest() { + service.revokeCommand(CommandTestFactory.ID); + + verify(service).createRevokeRequest(CommandTestFactory.ID); + } + + @Test + void shouldCallGrpc() { + service.revokeCommand(CommandTestFactory.ID); + + verify(commandServiceStub).revokeCommand(request); + } + + @Test + void shouldMapCommand() { + service.revokeCommand(CommandTestFactory.ID); + + verify(mapper).toCommand(any()); + } + } + } + + @Nested + @DisplayName("Get command") + class TestGetCommand { + + @Nested + class CreateRequest { + + @BeforeEach + void initTest() { + when(contextService.createCallContext()).thenReturn(grpcCallContext); + } + + @Test + void shouldReturnRequest() { + var request = service.createGetCommandRequest(CommandTestFactory.ID); + + assertThat(request).isNotNull().isInstanceOf(GrpcGetCommandRequest.class); + } + + @Test + void shouldHaveCallContext() { + var request = service.createGetCommandRequest(CommandTestFactory.ID); + + assertThat(request.getContext()).isNotNull().isSameAs(grpcCallContext); + verify(contextService).createCallContext(); + } + + @Test + void shouldHaveCommandId() { + var request = service.createGetCommandRequest(CommandTestFactory.ID); + + assertThat(request.getId()).isEqualTo(CommandTestFactory.ID); + } + } + + @DisplayName("send request") + @Nested + class SendRequest { + + private GrpcGetCommandRequest request = GrpcGetCommandRequest.newBuilder().build(); + + @BeforeEach + void initTest() { + doReturn(request).when(service).createGetCommandRequest(anyString()); + when(commandServiceStub.getCommand(any())).thenReturn(GrpcCommand.newBuilder().build()); + } + + @Test + void shouldCreateRequest() { + service.getCommand(CommandTestFactory.ID); + + verify(service).createGetCommandRequest(CommandTestFactory.ID); + } + + @Test + void shouldCallGrpc() { + service.getCommand(CommandTestFactory.ID); + + verify(commandServiceStub).getCommand(request); + } + + @Test + void shouldMapCommand() { + service.getCommand(CommandTestFactory.ID); + + verify(mapper).toCommand(any()); + } + } + } + + @Nested + class TestExistsPendingCommands { + + private final GrpcExistsPendingCommandsRequest request = GrpcExistsPendingCommandsRequest.newBuilder() + .setContext(grpcCallContext) + .setVorgangId(VorgangHeaderTestFactory.ID).build(); + + @BeforeEach + void mockServiceStub() { + when(commandServiceStub.existsPendingCommands(any())) + .thenReturn(GrpcExistsPendingCommandsResponse.newBuilder().setExistsPendingCommands(true).build()); + + when(contextService.createCallContext()).thenReturn(grpcCallContext); + } + + @Test + void shouldCallServiceStub() { + service.existsPendingCommands(VorgangHeaderTestFactory.ID); + + verify(commandServiceStub).existsPendingCommands(request); + } + + @Test + void shouldCallContextService() { + service.existsPendingCommands(VorgangHeaderTestFactory.ID); + + verify(contextService).createCallContext(); + } + + @Test + void shouldReturnValue() { + var result = service.existsPendingCommands(VorgangHeaderTestFactory.ID); + + assertThat(result).isTrue(); + } + } + + @Nested + class TestFindCommands { + + private final GrpcCommand grpcCommand = GrpcCommand.newBuilder().build(); + private final GrpcCommandsResponse response = GrpcCommandsResponse.newBuilder().addAllCommand(Collections.singleton(grpcCommand)).build(); + + @Captor // NOSONAR + private ArgumentCaptor<GrpcFindCommandsRequest> requestCaptor; + + @BeforeEach + void init() { + when(commandServiceStub.findCommands(any())).thenReturn(response); + when(contextService.createCallContext()).thenReturn(grpcCallContext); + } + + @Test + void shouldCallServiceStub() { + service.findCommands(VorgangHeaderTestFactory.ID, Optional.empty(), Optional.empty()); + + verify(commandServiceStub).findCommands(notNull()); + } + + @Test + void shouldRequestWithVorgangId() { + service.findCommands(VorgangHeaderTestFactory.ID, Optional.empty(), Optional.empty()); + + verify(commandServiceStub).findCommands(requestCaptor.capture()); + + assertThat(requestCaptor.getValue().getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldRequestWithStatus() { + service.findCommands(VorgangHeaderTestFactory.ID, Optional.of(CommandStatus.FINISHED), Optional.empty()); + + verify(commandServiceStub).findCommands(requestCaptor.capture()); + + assertThat(requestCaptor.getValue().getStatusList()).contains(CommandStatus.FINISHED.name()); + } + + @Test + void shouldRequestWithOrder() { + service.findCommands(VorgangHeaderTestFactory.ID, Optional.empty(), Optional.of(CommandOrder.REDIRECT_VORGANG)); + + verify(commandServiceStub).findCommands(requestCaptor.capture()); + + assertThat(requestCaptor.getValue().getOrder().name()).isEqualTo(CommandOrder.REDIRECT_VORGANG.name()); + } + + @Test + void shouldCallMapper() { + service.findCommands(VorgangHeaderTestFactory.ID, Optional.empty(), Optional.empty()).forEach(command -> { + }); + + verify(mapper).toCommand(grpcCommand); + } + } + + @Nested + class TestGetPendingCommands { + + private final GrpcGetPendingCommandsRequest request = GrpcGetPendingCommandsRequest.newBuilder() + .setContext(grpcCallContext) + .setVorgangId(VorgangHeaderTestFactory.ID).build(); + private final GrpcCommand grpcCommand = GrpcCommand.newBuilder().build(); + private final Set<GrpcCommand> commandList = Collections.singleton(grpcCommand); + private final GrpcGetPendingCommandsResponse response = GrpcGetPendingCommandsResponse.newBuilder().addAllCommand(commandList).build(); + private final Command command = CommandTestFactory.create(); + + @BeforeEach + void mockServiceStub() { + when(commandServiceStub.getPendingCommands(any())).thenReturn(response); + when(contextService.createCallContext()).thenReturn(grpcCallContext); + } + + @Test + void shouldCallServiceStub() { + service.getPendingCommands(VorgangHeaderTestFactory.ID); + + verify(commandServiceStub).getPendingCommands(request); + } + + @Test + void shouldCallContextService() { + service.getPendingCommands(VorgangHeaderTestFactory.ID); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallMapper() { + var result = service.getPendingCommands(VorgangHeaderTestFactory.ID); + forceStreamOperationsDone(result); + + verify(mapper).toCommand(grpcCommand); + } + + @Test + void shouldReturnValue() { + when(mapper.toCommand(any())).thenReturn(command); + + var result = service.getPendingCommands(VorgangHeaderTestFactory.ID); + + assertThat(result.collect(Collectors.toList()).get(0)).isEqualTo(command); + } + + private void forceStreamOperationsDone(Stream<Command> stream) { + stream.collect(Collectors.toList()).stream(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cbec114cd7a33ee409c9c272ffa909582aac69be --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandServiceTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class CommandServiceTest { + + @InjectMocks + private CommandService service; + @Mock + private CommandRemoteService remoteService; + + @Captor + private ArgumentCaptor<CreateCommand> createCommandCaptor; + + @Nested + class TestCreateCommand { + + private final CreateCommand command = CommandTestFactory.createCreateCommand(); + + @Nested + class TestWithCreateCommandOnly { + + @Test + void shouldCallRemoteService() { + service.createCommand(command); + + verify(remoteService).createCommand(any(CreateCommand.class)); + } + + @Test + void shouldHaveSetRelationVersion() { + service.createCommand(command); + + verify(remoteService).createCommand(createCommandCaptor.capture()); + assertThat(createCommandCaptor.getValue().getRelationVersion()).isEqualTo(CommandService.NO_RELATION_VERSION); + } + } + + @Nested + class TestWithRelationVersion { + + @Test + void shouldCallRemoteService() { + service.createCommand(command, CommandTestFactory.RELATION_VERSION); + + verify(remoteService).createCommand(any(CreateCommand.class)); + } + + @Test + void shouldHaveSetRelationVersion() { + service.createCommand(command, CommandTestFactory.RELATION_VERSION); + + verify(remoteService).createCommand(createCommandCaptor.capture()); + assertThat(createCommandCaptor.getValue().getRelationVersion()).isEqualTo(CommandTestFactory.RELATION_VERSION); + } + } + } + + @Nested + class TestCreateCommandWithItemName { + + private final CreateCommand command = CommandTestFactory.createCreateCommand(); + + @Nested + class WithoutValidation { + + @Test + void shouldCallRemoteService() { + service.createCommand(command, CommandTestFactory.RELATION_VERSION); + + verify(remoteService).createCommand(any(CreateCommand.class)); + } + + @Test + void shouldHaveSetRelationVersion() { + service.createCommand(command, CommandTestFactory.RELATION_VERSION); + + verify(remoteService).createCommand(createCommandCaptor.capture()); + assertThat(createCommandCaptor.getValue().getRelationVersion()).isEqualTo(CommandTestFactory.RELATION_VERSION); + } + } + } + + @Nested + class TestGetById { + + @Test + void shouldCallRemoteService() { + service.getById(CommandTestFactory.RELATION_ID); + + verify(remoteService).getCommand(any()); + } + } + + @Nested + class TestRevoke { + + @Test + void shouldCallRemoteService() { + service.revoke(CommandTestFactory.RELATION_ID); + + verify(remoteService).revokeCommand(CommandTestFactory.RELATION_ID); + } + } + + @Nested + class TestExistsPendingCommands { + + @Test + void shouldCallRemoteService() { + service.existsPendingCommands(VorgangHeaderTestFactory.ID); + + verify(remoteService).existsPendingCommands(VorgangHeaderTestFactory.ID); + } + } + + @Nested + class TestGetPendingCommands { + + @Test + void shouldCallRemoteService() { + service.getPendingCommands(VorgangHeaderTestFactory.ID); + + verify(remoteService).getPendingCommands(VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Find finished commands") + @Nested + class TestFindFinishedCommands { + + @Test + void shouldCallRemoteService() { + service.findFinishedCommands(VorgangHeaderTestFactory.ID); + + verify(remoteService).findCommands(VorgangHeaderTestFactory.ID, Optional.of(CommandStatus.FINISHED), Optional.empty()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d77c2ec82c55a316cf20cfcb744a7179386a762b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/CommandTestFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.UUID; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.forwarding.ForwardingTestFactory; + +public class CommandTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final String VORGANG_ID = VorgangHeaderTestFactory.ID; + public static final CommandStatus STATUS = CommandStatus.FINISHED; + public static final CommandOrder ORDER = CommandOrder.VORGANG_ANNEHMEN; + + public static final String RELATION_ID = ForwardingTestFactory.ID; + public static final long RELATION_VERSION = VorgangHeaderTestFactory.VERSION; + + public static Command create() { + return createBuilder().build(); + } + + public static Command.CommandBuilder createBuilder() { + return Command.builder() + .id(ID) + .vorgangId(VORGANG_ID) + .relationId(RELATION_ID) + .status(STATUS) + .order(ORDER) + .createdBy(UserProfileTestFactory.ID); + } + + public static CreateCommand createCreateCommand() { + return createCreateCommandBuilder().build(); + } + + public static CreateCommand.CreateCommandBuilder createCreateCommandBuilder() { + return CreateCommand.builder() + .vorgangId(VORGANG_ID) + .relationId(RELATION_ID) + .relationVersion(RELATION_VERSION) + .order(ORDER); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0fac6a84fdb88d6d35adbd421a8557017cf01aa5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandResponseTestFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommandResponse; + +public class GrpcCommandResponseTestFactory { + + public static final String COMMAND_ID = CommandTestFactory.ID; + public static final CommandStatus COMMAND_STATUS = CommandStatus.PENDING; + + public static GrpcCommandResponse create() { + return createBuilder().build(); + } + + public static GrpcCommandResponse.Builder createBuilder() { + return GrpcCommandResponse.newBuilder() + .setCommand(createGrpcCommand()); + } + + public static GrpcCommand createGrpcCommand() { + return GrpcCommand.newBuilder() + .setId(COMMAND_ID) + .setStatus(COMMAND_STATUS.name()) + .build(); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..04cc987a767a5b759b79599664c6af33a89dc208 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcCommandTestFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import java.util.UUID; + +import de.itvsh.goofy.common.GrpcCallContextTestFactory; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.command.GrpcCommand; +import de.itvsh.ozg.pluto.grpc.command.GrpcCreateCommandRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcOrder; + +public class GrpcCommandTestFactory { + + private static final String RELATION_ID = UUID.randomUUID().toString(); + private static final CommandStatus STATUS = CommandStatus.PENDING; + private static final GrpcOrder ORDER = GrpcOrder.RESEND_POSTFACH_MAIL; + private static final GrpcCallContext CALL_CONTEXT = GrpcCallContextTestFactory.create(); + + public static GrpcCommand create() { + return createBuilder().build(); + } + + public static GrpcCommand.Builder createBuilder() { + return GrpcCommand.newBuilder() + .setStatus(STATUS.name()) + .setRelationId(RELATION_ID) + .setOrder(ORDER); + } + + public static GrpcCreateCommandRequest createCommandRequest() { + return createCommandRequestBuilder().build(); + } + + public static GrpcCreateCommandRequest.Builder createCommandRequestBuilder() { + return GrpcCreateCommandRequest.newBuilder() + .setCallContext(CALL_CONTEXT) + .setRelationId(RELATION_ID) + .setOrder(ORDER); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcPostfachCommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcPostfachCommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..301137a91ab3db22039888261bc86f9fe61a4ddc --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/command/GrpcPostfachCommandTestFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.command; + +import de.itvsh.goofy.common.GrpcCallContextTestFactory; +import de.itvsh.goofy.postfach.PostfachMailTestFactory; +import de.itvsh.ozg.mail.postfach.GrpcResendPostfachMailRequest; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; + +public class GrpcPostfachCommandTestFactory { + + private static final String POSTFACH_ID = PostfachMailTestFactory.ID; + private static final String COMMAND_ID = CommandTestFactory.ID; + private static final GrpcCallContext CALL_CONTEXT = GrpcCallContextTestFactory.create(); + + public static GrpcResendPostfachMailRequest createResendPostfachMailRequest() { + return createResendPostfachMailRequestBuilder().build(); + } + + public static GrpcResendPostfachMailRequest.Builder createResendPostfachMailRequestBuilder() { + return GrpcResendPostfachMailRequest.newBuilder() + .setContext(CALL_CONTEXT) + .setPostfachMailId(POSTFACH_ID) + .setCommandId(COMMAND_ID); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..0365dea20587353016cbbaf3764a9f38907a936d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterITCase.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +@SpringBootTest +@AutoConfigureMockMvc +@WithMockUser +class DownloadTokenAuthenticationFilterITCase { + + @SpyBean + private DownloadTokenAuthenticationFilter filter; + + @Autowired + private MockMvc mockMvc; + + @Autowired + private DownloadTokenProperties downloadTokenProperties; + + @Test + void shouldNotCallFilter() throws Exception { + performRequest(DownloadTokenController.DOWNLOAD_TOKEN_PATH).andExpect(status().isBadRequest()); + + verify(filter, never()).doFilterInternal(any(), any(), any()); + } + + @Test + void shouldCallFilter() throws Exception { + String token = DownloadTokenTestFactory.createToken(downloadTokenProperties.getSecret(), downloadTokenProperties.getValidity()); + + performRequest(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token) + .andExpect(status().isOk()); + + verify(filter).doFilterInternal(any(), any(), any()); + } + + @Test + void shouldCallFilterWhenNoOrganisationseinheitIds() throws Exception { + String token = DownloadTokenTestFactory.createTokenBuilder(downloadTokenProperties.getSecret(), downloadTokenProperties.getValidity()) + .setClaims(DownloadTokenTestFactory.CLAIMS_WITHOUT_ORGANSIATIONSEINHEIT_IDS).compact(); + + performRequest(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token) + .andExpect(status().isOk()); + + verify(filter).doFilterInternal(any(), any(), any()); + } + + @Test + void shouldReturnUnauthorised() throws Exception { + String token = DownloadTokenTestFactory.createToken("badSecret", downloadTokenProperties.getValidity()); + + performRequest(DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + DownloadTokenController.PARAM_TOKEN + "=" + token) + .andExpect(status().isUnauthorized()); + } + + ResultActions performRequest(String path) throws Exception { + return mockMvc.perform(get(path)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f1d33d7a6fe127785885610691c7680b417befd5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenAuthenticationFilterTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.util.UUID; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +class DownloadTokenAuthenticationFilterTest { + + @InjectMocks + private DownloadTokenAuthenticationFilter filter; + @Mock + private FilterChain filterChain; + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + @Mock + private DownloadTokenService downloadTokenService; + + @Test + void shouldCallDoFilter() throws IOException, ServletException { + doFilterInternal(); + + verify(filterChain).doFilter(request, response); + } + + @Nested + class TestWithTokenParamter { + + final String TOKEN = UUID.randomUUID().toString(); + + @BeforeEach + void mockRequestParameter() { + when(request.getParameter(anyString())).thenReturn(TOKEN); + } + + @Test + void shouldVerifyTokenIfPresent() throws Exception { + doFilterInternal(); + + verify(downloadTokenService).handleToken(request, TOKEN); + } + } + + private void doFilterInternal() throws ServletException, IOException { + filter.doFilterInternal(request, response, filterChain); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d03b7d35726665147e4a790af2e6dfc561306684 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenControllerTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +class DownloadTokenControllerTest { + + @InjectMocks // NOSONAR + private DownloadTokenController controller; + @Mock + private DownloadTokenService service; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + final static String TOKEN = UUID.randomUUID().toString(); + + @BeforeEach + void mock() { + when(service.createToken(any())).thenReturn(TOKEN); + } + + @Test + void shouldGenerateToken() throws Exception { + callEndpoint(createRequest(BinaryFileTestFactory.ID)); + + verify(service).createToken(any()); + } + + @Test + void shouldGenerateTokenWhenNoFileId() throws Exception { + callEndpoint(createRequest(StringUtils.EMPTY)); + + verify(service).createToken(any()); + } + + @Test + void shouldHaveReponse() throws Exception { + callEndpoint(createRequest(BinaryFileTestFactory.ID)) + .andExpect(header().string("Location", "http://localhost" + DownloadTokenController.DOWNLOAD_TOKEN_PATH + "?" + + DownloadTokenController.PARAM_TOKEN + "=" + TOKEN)); + } + + private ResultActions callEndpoint(String content) throws Exception { + return mockMvc.perform( + post(DownloadTokenController.DOWNLOAD_TOKEN_PATH) + .contentType(MediaType.APPLICATION_JSON) + .content(content)) + .andExpect(status().isCreated()); + } + + private String createRequest(String fileId) { + return TestUtils.loadTextFile("jsonTemplates/downloadTokenRequest.json.tmpl", fileId); + } + +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bf9d88ebc233f3c1a16d8602a443717ba3ca3069 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenServiceTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static de.itvsh.goofy.JwtTokenUtil.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import com.auth0.jwt.exceptions.JWTVerificationException; + +import de.itvsh.goofy.JwtTokenUtil; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import io.jsonwebtoken.Claims; + +class DownloadTokenServiceTest { + + @InjectMocks + private DownloadTokenService service; + @Mock + private CurrentUserService userService; + @Mock + private JwtTokenUtil jwtTokenUtil; + @Mock + private HttpServletRequest request; + @Mock + private HttpServletResponse response; + + private final static String FAKE_TOKEN = "xxx"; + private final static Collection<String> ORGE_IDS = List.of("258994"); + + @Nested + class TestCreateToken { + + final FileId fileId = FileId.createNew(); + + final UserProfile user = UserProfileTestFactory.create(); + + @BeforeEach + void mockUserService() { + when(userService.getUser()).thenReturn(user); + } + + @Test + void shouldCallUserService() { + service.createToken(fileId); + + verify(userService).getUser(); + } + + @Test + void shouldCallJwtTokenUtil() { + service.createToken(fileId); + + verify(jwtTokenUtil).generateToken(fileId, user); + } + } + + @Nested + class TestHandleToken { + + @Test + void shouldVerifyTokenIfPresent() throws Exception { + mockClaims(FIRSTNAME_CLAIM, LASTNAME_CLAIM); + + handleToken(); + + verify(jwtTokenUtil).verifyToken(FAKE_TOKEN); + } + + @Test + void shouldGetUserFromTokenWithoutUserFirstname() { + mockClaims(null, FIRSTNAME_CLAIM); + + UserProfile user = service.getUserFromToken(FAKE_TOKEN).getUser(); + + assertThat(user).isNotNull(); + } + + @Test + void shouldGetUserFromTokenWithoutUserLastname() { + mockClaims(FIRSTNAME_CLAIM, null); + + UserProfile user = service.getUserFromToken(FAKE_TOKEN).getUser(); + + assertThat(user).isNotNull(); + } + + @Test + void shouldGetOrganisationseinheitIdsFromToken() { + mockClaims(FIRSTNAME_CLAIM, LASTNAME_CLAIM); + + UserProfile user = service.getUserFromToken(FAKE_TOKEN).getUser(); + + assertThat(user.getOrganisationseinheitIds()).isEqualTo(ORGE_IDS); + } + + private void mockClaims(String firstnameClaim, String lastnameClaim) { + Claims claims = mock(Claims.class); + + when(claims.get(FIRSTNAME_CLAIM, String.class)).thenReturn(firstnameClaim); + when(claims.get(LASTNAME_CLAIM, String.class)).thenReturn(lastnameClaim); + when(claims.get(USERID_CLAIM, String.class)).thenReturn(UserProfileTestFactory.ID.toString()); + + when(jwtTokenUtil.getOrganisationseinheitIdsFromToken(any())).thenReturn(ORGE_IDS); + when(jwtTokenUtil.getAllClaimsFromToken(any())).thenReturn(Optional.of(claims)); + } + + @Test + void shouldThrowExceptionOnInvalidToken() throws Exception { + doThrow(JWTVerificationException.class).when(jwtTokenUtil).verifyToken(anyString()); + + assertThrows(TechnicalException.class, () -> handleToken()); + } + + @Test + void shouldNotFilterOnInvalidToken() throws Exception { + doThrow(JWTVerificationException.class).when(jwtTokenUtil).verifyToken(anyString()); + + assertThrows(TechnicalException.class, () -> handleToken()); + } + + } + + void handleToken() { + service.handleToken(request, FAKE_TOKEN); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a359d826e46fa38969410c469a5f3cd5278ff863 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/downloadtoken/DownloadTokenTestFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.downloadtoken; + +import static de.itvsh.goofy.JwtTokenUtil.*; + +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import io.jsonwebtoken.JwtBuilder; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +public class DownloadTokenTestFactory { + static final String TYP = "typ"; + static final String SUBJECT = "subject"; + static final String FIRSTNAME_CLAIM_VALUE = UserProfileTestFactory.FIRSTNAME; + static final String LASTNAME_CLAIM_VALUE = UserProfileTestFactory.LASTNAME; + final static Collection<String> ORGE_IDS = List.of("258994"); + static final List<?> ROLE_CLAIM_VALUE = List.of(); + static final long VALIDITY = 5000; + static final Map<String, Object> CLAIMS = new HashMap<>(Map.of( + FIRSTNAME_CLAIM, FIRSTNAME_CLAIM_VALUE, + LASTNAME_CLAIM, LASTNAME_CLAIM_VALUE, + ROLE_CLAIM, ROLE_CLAIM_VALUE, + ORGANSIATIONSEINHEIT_IDS_CLAIM, ORGE_IDS)); + static final Map<String, Object> CLAIMS_WITHOUT_ORGANSIATIONSEINHEIT_IDS = new HashMap<>(Map.of( + FIRSTNAME_CLAIM, FIRSTNAME_CLAIM_VALUE, + LASTNAME_CLAIM, LASTNAME_CLAIM_VALUE, + ROLE_CLAIM, ROLE_CLAIM_VALUE)); + + public static String createToken(String secret, long validity) { + return createTokenBuilder(secret, validity).compact(); + } + + public static JwtBuilder createTokenBuilder(String secret, long validity) { + return Jwts.builder() + .setClaims(CLAIMS) + .setSubject(SUBJECT) + .setHeaderParam(TYP, TOKEN_TYPE) + .setIssuer(TOKEN_ISSUER).setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + validity)) + .setAudience(TOKEN_AUDIENCE) + .signWith(SignatureAlgorithm.HS512, secret.getBytes()); + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..43a942e5cb3b91fe34af064d0157411d759d17d7 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionControllerTest.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.Map; + +import javax.validation.ConstraintViolationException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.springframework.security.access.AccessDeniedException; + +import de.itvsh.goofy.common.binaryfile.DynamicViolationParameter; +import de.itvsh.kop.common.errorhandling.TechnicalException; + +class ExceptionControllerTest { + + @Spy + @InjectMocks + private ExceptionController exceptionController; + + @Nested + class TestHandleFunctionalException { + + private final FunctionalException exception = new FunctionalException(() -> ExceptionTestFactory.MESSAGE_CODE); + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionTestFactory.MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).isEqualTo(exception.getMessage()); + assertThat(error.getIssues().get(0).getMessage()).contains(exception.getExceptionId()); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(exception.getExceptionId()); + } + + private ApiError handleException() { + return exceptionController.handleFunctionalException(exception); + } + } + + @Nested + class TestHandleAccessDeniedException { + + private final AccessDeniedException exception = new AccessDeniedException(ExceptionTestFactory.MESSAGE); + + @BeforeEach + void mockExceptionId() { + doReturn(ExceptionTestFactory.EXCEPTION_ID).when(exceptionController).createExceptionId(); + } + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionController.ACCESS_DENIED_MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).contains(exception.getMessage()); + assertThat(error.getIssues().get(0).getMessage()).contains(ExceptionTestFactory.EXCEPTION_ID); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(ExceptionTestFactory.EXCEPTION_ID); + } + + private ApiError handleException() { + return exceptionController.handleAccessDeniedException(exception); + } + } + + @Nested + class TestHandleResourceNotFoundException { + + private final ResourceNotFoundException exception = new ResourceNotFoundException(String.class, 42L); + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionController.RESOURCE_NOT_FOUNT_MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).isEqualTo(exception.getMessage()); + assertThat(error.getIssues().get(0).getMessage()).contains(exception.getExceptionId()); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(exception.getExceptionId()); + } + + private ApiError handleException() { + return exceptionController.handleResourceNotFoundException(exception); + } + } + + @Nested + class TestContraintValidationException { + + private final ConstraintViolationException exception = new ConstraintViolationException(ExceptionTestFactory.MESSAGE, + Collections.singleton(ExceptionTestFactory.buildMockedConstraintViolation())); + + @BeforeEach + void mockExceptionId() { + doReturn(ExceptionTestFactory.EXCEPTION_ID).when(exceptionController).createExceptionId(); + } + + @Test + void shouldHaveField() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getField()).isEqualTo(ExceptionTestFactory.PATH_FIELD); + } + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionTestFactory.MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).isEqualTo(ExceptionTestFactory.MESSAGE); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(ExceptionTestFactory.EXCEPTION_ID); + } + + @Test + void shouldHaveIssueParameter() { + var error = handleException(); + + var issueParameter = error.getIssues().get(0).getParameters().get(0); + assertThat(issueParameter.getName()).isEqualTo(ExceptionTestFactory.PARAM_NAME); + assertThat(issueParameter.getValue()).isEqualTo(ExceptionTestFactory.PARAM_DYNAMIC_VALUE); + } + + @Nested + class TestWithDynamicPayload { + + private final DynamicViolationParameter dynamicViolationParameter = DynamicViolationParameter.builder() + .map(Map.of(ExceptionTestFactory.PARAM_NAME, ExceptionTestFactory.PARAM_DYNAMIC_VALUE)).build(); + private final ConstraintViolationException exceptionWithDynamicPayload = new ConstraintViolationException(ExceptionTestFactory.MESSAGE, + Collections.singleton(ExceptionTestFactory.buildMockedConstraintViolationWithDynamicPayload(dynamicViolationParameter))); + + @Test + void shouldHaveReplacedParams() { + var error = handleExceptionWithDynamicPayload(); + + var issueParameter = error.getIssues().get(0).getParameters().get(0); + assertThat(issueParameter.getName()).isEqualTo(ExceptionTestFactory.PARAM_NAME); + assertThat(issueParameter.getValue()).isEqualTo(ExceptionTestFactory.PARAM_DYNAMIC_VALUE); + } + + private ApiError handleExceptionWithDynamicPayload() { + return exceptionController.handleConstraintViolationException(exceptionWithDynamicPayload); + } + } + + private ApiError handleException() { + return exceptionController.handleConstraintViolationException(exception); + } + } + + @Nested + class TestHandleTechnicalException { + + private final TechnicalException exception = new TechnicalException(ExceptionTestFactory.MESSAGE, new Throwable()); + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionController.RUNTIME_MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).isEqualTo(exception.getMessage()); + assertThat(error.getIssues().get(0).getMessage()).contains(exception.getExceptionId()); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(exception.getExceptionId()); + } + + private ApiError handleException() { + return exceptionController.handleTechnicalException(exception); + } + } + + @Nested + class TestHandleRuntimeException { + + private final RuntimeException exception = new RuntimeException(ExceptionTestFactory.MESSAGE); + + @BeforeEach + void mockExceptionId() { + doReturn(ExceptionTestFactory.EXCEPTION_ID).when(exceptionController).createExceptionId(); + } + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(ExceptionController.RUNTIME_MESSAGE_CODE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).contains(exception.getMessage()); + assertThat(error.getIssues().get(0).getMessage()).contains(ExceptionTestFactory.EXCEPTION_ID); + } + + @Test + void shouldHaveExceptionId() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getExceptionId()).isEqualTo(ExceptionTestFactory.EXCEPTION_ID); + } + + private ApiError handleException() { + return exceptionController.handleRuntimeException(exception); + } + } + + @DisplayName("Handle ServiceUnavailableException") + @Nested + class TestHandleServiceUnavailableException { + + private final ServiceUnavailableException exception = new ServiceUnavailableException(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE, + new Throwable()); + + @Test + void shouldHaveMessageCode() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessageCode()).isEqualTo(MessageCode.USER_MANAGER_SERVICE_UNAVAILABLE); + } + + @Test + void shouldHaveMessage() { + var error = handleException(); + + assertThat(error.getIssues()).hasSize(1); + assertThat(error.getIssues().get(0).getMessage()).isEqualTo("Service Unavailable"); + } + + private ApiError handleException() { + return exceptionController.handleServiceUnavailableException(exception); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..fb78df1413c5d3000ca330c56775e1c6a532e886 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/ExceptionTestFactory.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Map; +import java.util.UUID; + +import javax.validation.ConstraintViolation; +import javax.validation.Path; +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.engine.HibernateConstraintViolation; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.binaryfile.DynamicViolationParameter; + +public class ExceptionTestFactory { + + static final String EXCEPTION_ID = UUID.randomUUID().toString(); + + static final String PARAM_NAME = "param"; + static final String PARAM_VALUE = "3"; + static final String PARAM_DYNAMIC_VALUE = "20"; + static final String MESSAGE = LoremIpsum.getInstance().getWords(5); + static final String MESSAGE_CODE = "message.code"; + private static final String PATH_SUFFIX = "createCommandByRelation"; + static final String PATH_FIELD = "command.wiedervorlage.betreff"; + private static final String PATH = PATH_SUFFIX + "." + PATH_FIELD; + + public static ConstraintViolation<?> buildMockedConstraintViolation() { + return ExceptionTestFactory.buildMockedConstraintViolationWithDynamicPayload(null); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static <T> ConstraintViolation<T> buildMockedConstraintViolationWithDynamicPayload(DynamicViolationParameter dynamicViolationParameter) { + ConstraintViolation violation = mock(ConstraintViolation.class); + HibernateConstraintViolation hibernateViolation = mock(HibernateConstraintViolation.class); + ConstraintDescriptor constraintDescriptor = mock(ConstraintDescriptor.class); + + var path = mock(Path.class); + when(path.toString()).thenReturn(PATH); + when(violation.getPropertyPath()).thenReturn(path); + when(violation.getMessageTemplate()).thenReturn("{" + MESSAGE_CODE + "}"); + when(violation.getMessage()).thenReturn(MESSAGE); + when(violation.getConstraintDescriptor()).thenReturn(constraintDescriptor); + when(constraintDescriptor.getAttributes()).thenReturn(Map.of(PARAM_NAME, PARAM_DYNAMIC_VALUE)); + when(violation.unwrap(any())).thenReturn(hibernateViolation); + when(hibernateViolation.getDynamicPayload(any())).thenReturn(dynamicViolationParameter); + + return violation; + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/FunctionalExceptionTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/FunctionalExceptionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..008a38dd297e49b24f51ace309ff66613293b5c5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/FunctionalExceptionTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import static org.assertj.core.api.Assertions.*; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; + +class FunctionalExceptionTest { + + @Test + void shouldIncludeExceptionId() { + var exception = new FunctionalException(() -> StringUtils.EMPTY); + + assertThat(exception.getMessage()).contains(exception.getExceptionId()); + assertThat(exception.getExceptionId()).isNotNull(); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3266409a65934538d5331351df8a13ddeb3de5f6 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionControllerTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.*; + +import io.grpc.StatusRuntimeException; + +class GrpcExceptionControllerTest { + + @InjectMocks + private GrpcExceptionController grpcExceptionController; + + @Nested + class TestNotFoundStatusException { + + private final StatusRuntimeException notFoundException = GrpcExceptionTestFactory.createGrpcNotFoundStatusRuntimeException(); + + @Test + void shouldHaveMessageCode() { + var response = grpcExceptionController.handleStatusRuntimeException(notFoundException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessageCode()) + .isEqualTo(GrpcExceptionController.GRPC_NOT_FOUND_ERROR_CODE_KEY.getErrorCode()); + } + + @Test + void shouldHaveMessage() { + var response = grpcExceptionController.handleStatusRuntimeException(notFoundException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessage()).isEqualTo("NOT_FOUND: " + GrpcExceptionTestFactory.NOT_FOUND_DESCRIPTION); + } + + @Test + void shouldHaveHttpStatusCode() { + var response = grpcExceptionController.handleStatusRuntimeException(notFoundException); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + void shouldNotHaveExceptionId() { + var response = grpcExceptionController.handleStatusRuntimeException(notFoundException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isNull(); + } + + @Nested + class TestWithMetaData { + + private final StatusRuntimeException notFoundExceptionWithMetadata = GrpcExceptionTestFactory + .createGrpcNotFoundStatusException(GrpcExceptionTestFactory.createMetaData()); + + @Test + void shouldHaveExceptionId() { + var response = grpcExceptionController.handleStatusRuntimeException(notFoundExceptionWithMetadata); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isEqualTo(GrpcExceptionTestFactory.METADATA_EXCEPTION_ID_VALUE); + } + } + } + + @Nested + class TestInternalStatusException { + + private final StatusRuntimeException internalException = GrpcExceptionTestFactory.createGrpcInternalStatusException(); + + @Test + void shouldHaveMessageCode() { + var response = grpcExceptionController.handleStatusRuntimeException(internalException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessageCode()) + .isEqualTo(GrpcExceptionController.GRPC_INTERNAL_ERROR_CODE_KEY.getErrorCode()); + } + + @Test + void shouldHaveMessage() { + var response = grpcExceptionController.handleStatusRuntimeException(internalException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessage()).isEqualTo("INTERNAL: " + GrpcExceptionTestFactory.TECHNICAL_DESCRIPTION); + } + + @Test + void shouldHaveHttpStatusCode() { + var response = grpcExceptionController.handleStatusRuntimeException(internalException); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); + } + + @Test + void shouldNotHaveExceptionId() { + var response = grpcExceptionController.handleStatusRuntimeException(internalException); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isNull(); + } + + @Nested + class TestWithMetaData { + + private final StatusRuntimeException internalExceptionWithMetadata = GrpcExceptionTestFactory + .createGrpcInternalStatusException(GrpcExceptionTestFactory.createMetaData()); + + @Test + void shouldHaveErrorCodeAsMessageCode() { + var response = grpcExceptionController.handleStatusRuntimeException(internalExceptionWithMetadata); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessageCode()).isEqualTo(GrpcExceptionTestFactory.METADATA_ERROR_CODE_VALUE); + } + + @Test + void shouldHaveExceptionId() { + var response = grpcExceptionController.handleStatusRuntimeException(internalExceptionWithMetadata); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isEqualTo(GrpcExceptionTestFactory.METADATA_EXCEPTION_ID_VALUE); + } + } + } + + @Nested + class TestStatusUnavailableException { + + private final StatusRuntimeException unavailableException = GrpcExceptionTestFactory.createGrpcUnavailableStatusException(); + + @Test + void shouldHaveMessageCode() { + var response = handleUnavailableException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessageCode()) + .isEqualTo(GrpcExceptionController.GRPC_INTERNAL_ERROR_CODE_KEY.getErrorCode()); + } + + @Test + void shouldHaveMessage() { + var response = handleUnavailableException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessage()).isEqualTo("UNAVAILABLE"); + } + + @Test + void shouldHaveHttpStatusCode() { + var response = handleUnavailableException(); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + } + + @Test + void shouldNotHaveExceptionId() { + var response = handleUnavailableException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isNotNull(); + } + + private ResponseEntity<ApiError> handleUnavailableException() { + return handleException(unavailableException); + } + + @Nested + class TestWithMetadata { + + private final StatusRuntimeException unavailableExceptionWithMetadata = GrpcExceptionTestFactory + .createGrpcUnavailableStatusException(GrpcExceptionTestFactory.createMetaData()); + + @Test + void shouldNotHaveExceptionId() { + var response = handleUnavailableException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isEqualTo(GrpcExceptionTestFactory.METADATA_EXCEPTION_ID_VALUE); + } + + private ResponseEntity<ApiError> handleUnavailableException() { + return handleException(unavailableExceptionWithMetadata); + } + } + } + + @Nested + class TestPermissionDeniedException { + + private final StatusRuntimeException permissionDeniedException = GrpcExceptionTestFactory.createGrpcPermissionDeniedStatusException(); + + @Test + void shouldHaveMessageCode() { + var response = handlePermissionDeniedException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessageCode()) + .isEqualTo(GrpcExceptionController.GRPC_PERMISSION_DENIED_CODE_KEY.getErrorCode()); + } + + @Test + void shouldHaveMessage() { + var response = handlePermissionDeniedException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getMessage()) + .isEqualTo("PERMISSION_DENIED: " + GrpcExceptionTestFactory.PERMISSION_DENIED_DESCRIPTION); + } + + @Test + void shouldHaveHttpStatusCode() { + var response = handlePermissionDeniedException(); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); + } + + @Test + void shouldNotHaveExceptionId() { + var response = handlePermissionDeniedException(); + + assertThat(response.getBody().getIssues()).hasSize(1); + assertThat(response.getBody().getIssues().get(0).getExceptionId()).isNotNull(); + } + + private ResponseEntity<ApiError> handlePermissionDeniedException() { + return handleException(permissionDeniedException); + } + } + + private ResponseEntity<ApiError> handleException(StatusRuntimeException exception) { + return grpcExceptionController.handleStatusRuntimeException(exception); + } + + @Nested + class TestMapToIssueParams { + + @Test + void shouldMapParams() { + var params = grpcExceptionController.mapToIssueParams( + GrpcExceptionTestFactory.createMetaDataWithSingleEntry("param_key", "param_value")); + + assertThat(params).hasSize(1); + assertThat(params.get(0).getName()).isEqualTo("param_key"); + assertThat(params.get(0).getValue()).isEqualTo("param_value"); + } + + @Test + void shouldNotMapErrorCode() { + var metadata = GrpcExceptionTestFactory.createMetaDataWithSingleEntry( + GrpcExceptionController.KEY_ERROR_CODE, GrpcExceptionTestFactory.METADATA_ERROR_CODE_VALUE); + + var params = grpcExceptionController.mapToIssueParams(metadata); + + assertThat(params).isEmpty(); + } + + @Test + void shouldNotMapExceptionId() { + var metadata = GrpcExceptionTestFactory.createMetaDataWithSingleEntry( + GrpcExceptionController.KEY_EXCEPTION_ID, GrpcExceptionTestFactory.METADATA_EXCEPTION_ID_VALUE); + + var params = grpcExceptionController.mapToIssueParams(metadata); + + assertThat(params).isEmpty(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0ec55dbaa1237e010c71bca0819075447f13a602 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/errorhandling/GrpcExceptionTestFactory.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.errorhandling; + +import org.springframework.security.access.AccessDeniedException; + +import io.grpc.Metadata; +import io.grpc.Metadata.Key; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; + +public class GrpcExceptionTestFactory { + + public static final String NOT_FOUND_DESCRIPTION = "Not Found Exception message"; + public static final String TECHNICAL_DESCRIPTION = "Technical Exception message"; + public static final String PERMISSION_DENIED_DESCRIPTION = "PermissionsDenied Exception message"; + public static final String PERMISSION_DENIED_CAUSE_MSG = "AccessDenied cause message"; + + public static final String METADATA_ERROR_CODE_VALUE = "Error_Code"; + public static final String METADATA_EXCEPTION_ID_VALUE = "42"; + + public static StatusRuntimeException createGrpcUnavailableStatusException(Metadata metadata) { + return new StatusRuntimeException(buildUnavailableStatus(), metadata); + } + + public static StatusRuntimeException createGrpcUnavailableStatusException() { + return new StatusRuntimeException(buildUnavailableStatus()); + } + + public static Status buildUnavailableStatus() { + return Status.UNAVAILABLE.withCause(new RuntimeException()); + } + + public static StatusRuntimeException createGrpcNotFoundStatusException(Metadata metadata) { + return new StatusRuntimeException(buildNotFoundStatus(), metadata); + } + + public static StatusRuntimeException createGrpcNotFoundStatusRuntimeException() { + return new StatusRuntimeException(buildNotFoundStatus()); + } + + private static Status buildNotFoundStatus() { + return Status.NOT_FOUND.withDescription(NOT_FOUND_DESCRIPTION).withCause(new RuntimeException()); + } + + public static StatusRuntimeException createGrpcInternalStatusException(Metadata metaData) { + return new StatusRuntimeException(buildInternalStatus(), metaData); + } + + public static StatusRuntimeException createGrpcInternalStatusException() { + return new StatusRuntimeException(buildInternalStatus()); + } + + private static Status buildInternalStatus() { + return Status.INTERNAL.withDescription(TECHNICAL_DESCRIPTION).withCause(new RuntimeException()); + } + + public static StatusRuntimeException createGrpcPermissionDeniedStatusException() { + return new StatusRuntimeException(buildPermissionDeniedStatus()); + } + + public static Status buildPermissionDeniedStatus() { + return Status.PERMISSION_DENIED.withDescription(PERMISSION_DENIED_DESCRIPTION) + .withCause(new AccessDeniedException(PERMISSION_DENIED_CAUSE_MSG)); + } + + public static Metadata createMetaData() { + Metadata data = new Metadata(); + data.put(Key.of(GrpcExceptionController.KEY_ERROR_CODE, Metadata.ASCII_STRING_MARSHALLER), METADATA_ERROR_CODE_VALUE); + data.put(Key.of(GrpcExceptionController.KEY_EXCEPTION_ID, Metadata.ASCII_STRING_MARSHALLER), METADATA_EXCEPTION_ID_VALUE); + return data; + } + + public static Metadata createMetaDataWithSingleEntry(String key, String value) { + Metadata data = new Metadata(); + data.put(Key.of(key, Metadata.ASCII_STRING_MARSHALLER), value); + return data; + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/file/GrpcOzgFileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/file/GrpcOzgFileTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..db54d831cce4895b41a7fb5c947ab98dde836f2a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/file/GrpcOzgFileTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; + +public class GrpcOzgFileTestFactory { + + static final String ID = OzgFileTestFactory.ID.toString(); + static final String NAME = OzgFileTestFactory.NAME; + static final long SIZE = OzgFileTestFactory.SIZE; + + public static GrpcOzgFile create() { + return createBuilder().build(); + } + + public static GrpcOzgFile.Builder createBuilder() { + return GrpcOzgFile.newBuilder() + .setId(ID) + .setName(NAME) + .setSize(SIZE) + .setContentType(OzgFileTestFactory.CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..10a6a542057974e39d81fa019cb14dbd47b1d1c6 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileMapperTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class OzgFileMapperTest { + + private final OzgFileMapper mapper = Mappers.getMapper(OzgFileMapper.class); + + @Test + void shouldMap() { + var file = mapper.toFile(GrpcOzgFileTestFactory.create()); + + assertThat(file).isNotNull().usingRecursiveComparison().isEqualTo(OzgFileTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00885ed7a141b8139785a47bb9d3208011905421 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileRemoteServiceTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.attachment.GrpcGetAttachmentsResponseTestFactory; +import de.itvsh.goofy.common.GrpcCallContextTestFactory; +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.representation.GrpcGetRepresentationsResponseTestFactory; +import de.itvsh.goofy.vorgang.EingangTestFactory; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; +import de.itvsh.ozg.pluto.grpc.file.FileServiceGrpc.FileServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetAttachmentsRequest; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetAttachmentsResponse; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetRepresentationsRequest; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetRepresentationsResponse; + +class OzgFileRemoteServiceTest { + + @InjectMocks + private OzgFileRemoteService remoteService; + @Mock + private FileServiceBlockingStub serviceStub; + @Mock + private OzgFileMapper mapper; + @Mock + private ContextService contextService; + + private GrpcCallContext callContext = GrpcCallContextTestFactory.create(); + + @BeforeEach + void mockContextService() { + when(contextService.createCallContext()).thenReturn(callContext); + } + + @Nested + class TestGetAttachmentsByEingang { + + private final GrpcGetAttachmentsResponse response = GrpcGetAttachmentsResponseTestFactory.create(); + + @BeforeEach + void mock() { + when(serviceStub.getAttachments(any())).thenReturn(response); + } + + @Test + void shouldCallContextService() { + doServiceCall(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallFileStub() { + doServiceCall(); + + verify(serviceStub).getAttachments(any(GrpcGetAttachmentsRequest.class)); + } + + @Test + void shouldCallMapper() { + var ozgFileAsStream = doServiceCall(); + ozgFileAsStream = forceActionsDone(ozgFileAsStream); + + verify(mapper).toFile(GrpcOzgFileTestFactory.create()); + } + + private Stream<OzgFile> forceActionsDone(Stream<OzgFile> stream) { + return stream.collect(Collectors.toList()).stream(); + } + + private Stream<OzgFile> doServiceCall() { + return remoteService.getAttachmentsByEingang(EingangTestFactory.ID); + } + } + + @Nested + class TestGetRepresentationsByEingang { + + private final GrpcGetRepresentationsResponse response = GrpcGetRepresentationsResponseTestFactory.create(); + + @BeforeEach + void mock() { + when(serviceStub.getRepresentations(any())).thenReturn(response); + } + + @Test + void shouldCallContextService() { + doServiceCall(); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallFileStub() { + doServiceCall(); + + verify(serviceStub).getRepresentations(any(GrpcGetRepresentationsRequest.class)); + } + + @Test + void shouldCallMapper() { + var ozgFileAsStream = doServiceCall(); + ozgFileAsStream = forceActionsDone(ozgFileAsStream); + + verify(mapper).toFile(GrpcOzgFileTestFactory.create()); + } + + private Stream<OzgFile> forceActionsDone(Stream<OzgFile> stream) { + return stream.collect(Collectors.toList()).stream(); + } + + private Stream<OzgFile> doServiceCall() { + return remoteService.getRepresentationsByEingang(EingangTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3fe599a9ea69e15dd60f4bf4623d6bd4c8532498 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileServiceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.vorgang.EingangTestFactory; + +class OzgFileServiceTest { + + @InjectMocks + private OzgFileService service; + @Mock + private OzgFileRemoteService remoteService; + + @Nested + class TestGetAttachmentsByEingang { + + @Test + void shouldCallRemoteService() { + service.getAttachmentsByEingang(EingangTestFactory.ID); + + verify(remoteService).getAttachmentsByEingang(EingangTestFactory.ID); + } + } + + @Nested + class TestGetRepresentationsByEingang { + + @Test + void shouldCallRemoteService() { + service.getRepresentationsByEingang(EingangTestFactory.ID); + + verify(remoteService).getRepresentationsByEingang(EingangTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..00e17cc371bc2e42c7148425e3d801880b93cb4d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/file/OzgFileTestFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.file; + +import java.util.UUID; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.binaryfile.FileId; + +public class OzgFileTestFactory { + + public static final FileId ID = FileId.from(UUID.randomUUID().toString()); + public static final String NAME = BinaryFileTestFactory.NAME; + public static final long SIZE = BinaryFileTestFactory.SIZE; + public static final String CONTENT_TYPE = BinaryFileTestFactory.CONTENT_TYPE; + + public static OzgFile create() { + return createBuilder().build(); + } + + public static OzgFile.OzgFileBuilder createBuilder() { + return OzgFile.builder() + .id(ID) + .name(NAME) + .size(SIZE) + .contentType(CONTENT_TYPE); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/CurrentUserServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/CurrentUserServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9ab969601d9b8db47d6fe13b79b8c1254d5eedb0 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/CurrentUserServiceTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Spy; + +class CurrentUserServiceTest { + + @Spy + private CurrentUserService service; + + @Nested + class TestGetOrganisationseinheit { + + @Test + void shouldReturnOrganisationseinheitIdsFromMap() { + Map<String, Object> claims = Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, List.of("1", "2")); + + var result = service.getOrganisationseinheitId(claims); + + assertThat(result).contains("1").contains("2"); + } + + @Test + void shouldReturnOrgaIdAsString() { + var result = service.getOrganisationseinheitId(Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, List.of("1", 2))); + + assertThat(result).contains("1").contains("2"); + } + + @Test + void shouldReturnEmptyList() { + Map<String, Object> claims = Map.of(CurrentUserService.USER_ATTRIBUTE_ORGANISATIONSEINHEIT_ID, Collections.emptyList()); + + var result = service.getOrganisationseinheitId(claims); + + assertThat(result).isEmpty(); + } + + @Test + void shouldReturnEmptyListIfNotExists() { + var result = service.getOrganisationseinheitId(Collections.emptyMap()); + + assertThat(result).isEmpty(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d4aa272f6699fc73f3595545e4ebce88dcbd44f3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/GrpcUserTestFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Collections; +import java.util.UUID; + +import de.itvsh.ozg.pluto.grpc.command.GrpcUser; + +public class GrpcUserTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final String NAME = UserProfileTestFactory.FULLNAME; + public static final String ROLE = UserProfileTestFactory.ROLE; + + public static GrpcUser create() { + return createBuilder().build(); + } + + public static GrpcUser.Builder createBuilder() { + return GrpcUser.newBuilder() + .setId(ID) + .setName(NAME) + .addAllRoles(Collections.singleton(ROLE)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0996dce79bae9174a0b708d289ae15cc3063d615 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserProfileTestFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import java.util.Collections; +import java.util.UUID; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import de.itvsh.goofy.vorgang.ZustaendigeStelleTestFactory; + +public class UserProfileTestFactory { + + public static final UserId ID = UserId.from(UUID.randomUUID().toString()); + public static final String FIRSTNAME = "Vaneßa"; + public static final String LASTNAME = "Täst"; + public static final String FULLNAME = String.format("%s %s", FIRSTNAME, LASTNAME); + public static final String ROLE = "TEST_USER"; + public static final GrantedAuthority AUTHORITY = new SimpleGrantedAuthority(ROLE); + + public static final String SYSTEM_USER = "system_user_example"; + + public static UserProfile create() { + return createBuilder().build(); + } + + public static UserProfile.UserProfileBuilder createBuilder() { + return UserProfile.builder() + .id(ID) + .firstName(FIRSTNAME) + .lastName(LASTNAME) + .organisationseinheitId(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID) + .authorities(Collections.singleton(AUTHORITY)); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..704771cf430758ed65f82454f20723500dc5fd35 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserRemoteServiceTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import de.itvsh.goofy.common.errorhandling.ServiceUnavailableException; + +class UserRemoteServiceTest { + + @Spy + @InjectMocks + private UserRemoteService service; + @Mock + private UserManagerProperties userManagerProperties; + @Mock + private UserManagerUrlProvider userManagerUrlProvider; + + @Mock + private RestTemplate restTemplate; + + @DisplayName("Get userId") + @Nested + class TestGetUserId { + + private final String internalUrlTemplate = "DummyInternalUrlTemplate"; + + @DisplayName("with configured usermanager") + @Nested + class TestWithConfiguredUserManager { + + @BeforeEach + void mock() { + when(userManagerProperties.getFullInternalUrlTemplate()).thenReturn(internalUrlTemplate); + when(userManagerUrlProvider.isConfiguredForInternalUserId()).thenReturn(true); + } + + @DisplayName("on valid response") + @Nested + class TestSuccess { + + @BeforeEach + void mock() { + when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenReturn(UserProfileTestFactory.ID.toString()); + } + + @Test + void shouldReturnResponseAsUserId() { + var userId = service.getUserId(UserProfileTestFactory.ID); + + assertThat(userId).hasValue(UserProfileTestFactory.ID); + } + } + + @DisplayName("on error response") + @Nested + class TestErrorCases { + + @Test + void shouldHandleEmptyValue() { + when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenReturn(""); + + var res = service.getUserId(UserProfileTestFactory.ID); + + assertThat(res).isNotPresent(); + } + + @Test + void shouldHandleError() { + when(restTemplate.getForObject(anyString(), eq(String.class), anyString())).thenThrow(new RestClientException("Test error")); + + var res = service.getUserId(UserProfileTestFactory.ID); + + assertThat(res).isNotPresent(); + } + } + } + + @DisplayName("with not configured usermanager") + @Nested + class TestOnNotConfiguredUserManager { + + @BeforeEach + void mock() { + when(userManagerUrlProvider.isConfiguredForInternalUserId()).thenReturn(false); + } + + @Test + void shouldNotCallUserManagerProperties() { + service.getUserId(UserProfileTestFactory.ID); + + verify(userManagerProperties, never()).getFullInternalUrlTemplate(); + } + + @Test + void shouldReturnEmptyOptional() { + var user = service.getUserId(UserProfileTestFactory.ID); + + assertThat(user).isNotPresent(); + } + } + } + + @DisplayName("Get user") + @Nested + class TestGetUser { + + private final String profileUri = "DummyProfileTemplate/" + UserProfileTestFactory.ID; + private final String dummyToken = "Token"; + + @BeforeEach + void mock() { + doReturn(profileUri).when(service).buildUserProfileUri(any()); + doReturn(dummyToken).when(service).getToken(); + } + + @DisplayName("on valid response") + @Nested + class TestOnValidResponse { + + private final Map<String, Object> bodyMap = new LinkedHashMap<>(Map.of(UserRemoteService.FIRST_NAME_KEY, UserProfileTestFactory.FIRSTNAME, + UserRemoteService.LAST_NAME_KEY, UserProfileTestFactory.LASTNAME)); + private final ResponseEntity<Object> response = new ResponseEntity<>(bodyMap, HttpStatus.OK); + + @BeforeEach + void mock() { + when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenReturn(response); + } + + @Test + void shouldCallRestTemplate() { + var headers = new HttpHeaders(); + headers.add("Authorization", "Bearer " + dummyToken); + var httpEntity = new HttpEntity<>(headers); + + service.getUser(UserProfileTestFactory.ID); + + verify(restTemplate).exchange(profileUri, HttpMethod.GET, httpEntity, Object.class); + } + + @Test + void shouldBuildUrl() { + service.getUser(UserProfileTestFactory.ID); + + verify(service).buildUserProfileUri(UserProfileTestFactory.ID); + } + + @Test + void shouldReturnUser() { + var loadedUser = service.getUser(UserProfileTestFactory.ID); + + assertThat(loadedUser.getFirstName()).isEqualTo(UserProfileTestFactory.FIRSTNAME); + assertThat(loadedUser.getLastName()).isEqualTo(UserProfileTestFactory.LASTNAME); + } + } + + @DisplayName("on error response") + @Nested + class TestOnErrorResponse { + + private final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(HttpStatus.SERVICE_UNAVAILABLE, + "Test error"); + private final IllegalArgumentException illegalArgumentException = new IllegalArgumentException(); + + private final HttpClientErrorException notFoundException = new HttpClientErrorException(HttpStatus.NOT_FOUND, "Test error"); + + @Test + void shouldThrowServiceUnavailablExceptionOnException() { + when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(httpClientErrorException); + + assertThatThrownBy(() -> service.getUser(UserProfileTestFactory.ID)).isInstanceOf(ServiceUnavailableException.class) + .hasCause(httpClientErrorException); + } + + @Test + void shouldThrowServiceUnavailablExceptionOnIlleglaArgumentException() { + when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(illegalArgumentException); + + assertThatThrownBy(() -> service.getUser(UserProfileTestFactory.ID)).isInstanceOf(ServiceUnavailableException.class) + .hasCause(illegalArgumentException); + } + + @Test + void shouldReturnEmptyOptionalOnNotFoundException() { + when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(), eq(Object.class))).thenThrow(notFoundException); + + var user = service.getUser(UserProfileTestFactory.ID); + + assertThat(user).isNull(); + } + } + } + + @DisplayName("Build user profile uri") + @Nested + class TestBuildUserProfileUri { + + private final String profileUriTemplate = "DummyProfileTemplate/%s"; + + @BeforeEach + void mock() { + when(userManagerUrlProvider.getInternalUserIdTemplate()).thenReturn(profileUriTemplate); + } + + @Test + void shouldCallUserManagerUrlProvider() { + service.buildUserProfileUri(UserProfileTestFactory.ID); + + verify(userManagerUrlProvider).getInternalUserIdTemplate(); + } + + @Test + void shouldReturnUserProfileUri() { + var uri = service.buildUserProfileUri(UserProfileTestFactory.ID); + + assertThat(uri).isEqualTo("DummyProfileTemplate/" + UserProfileTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d8092e887d3b4bd0bb20b48a159fa994a3014915 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/common/user/UserServiceTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.common.user; + +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +class UserServiceTest { + + @InjectMocks + private UserService service; + @Mock + private UserRemoteService remoteService; + + @DisplayName("Get by id") + @Nested + class TestGetById { + + @Test + void shouldCallRemoteService() { + service.getById(UserProfileTestFactory.ID); + + verify(remoteService).getUser(UserProfileTestFactory.ID); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieCommandHandlerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieCommandHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..449c0f20f288ed2ce2a01dd8836930295f524b4d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieCommandHandlerTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.postfach.PostfachMail; +import de.itvsh.goofy.wiedervorlage.Wiedervorlage; + +class HistorieCommandHandlerTest { + + private HistorieCommandHandler orderTranslator = new HistorieCommandHandler(); + + @DisplayName("postfach nachricht verification") + @Nested + class TestIsOutgoingPostfachNachrichtenByMailService { + + private final Command matchingCommand = CommandTestFactory.createBuilder() + .order(CommandOrder.CREATE_ATTACHED_ITEM) + .body(Map.of("item", Map.of(HistorieCommandHandler.DIRECTION, HistorieCommandHandler.DIRECTION_OUTGOING), + HistorieCommandHandler.CLIENT, HistorieCommandHandler.MAIL_SERVICE)) + .build(); + + @DisplayName("should return true if the command is CREATED_ATTACHED_ITEM order outgoing by mailserice") + @Test + void shouldReturnTrue() { + var result = orderTranslator.isOutgoingPostfachNachrichtByMailService(matchingCommand); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnFalseOnIncoming() { + var nonMatchingCommand = matchingCommand.toBuilder() + .body(Map.of("item", Map.of(HistorieCommandHandler.DIRECTION, HistorieCommandHandler.DIRECTION_INCOMMING), + HistorieCommandHandler.CLIENT, HistorieCommandHandler.MAIL_SERVICE)) + .build(); + + var result = orderTranslator.isOutgoingPostfachNachrichtByMailService(nonMatchingCommand); + + assertThat(result).isFalse(); + } + + @Test + void shouldReturnFalseOnOtherClient() { + var nonMatchingCommand = matchingCommand.toBuilder() + .body(Map.of("item", Map.of(HistorieCommandHandler.DIRECTION, HistorieCommandHandler.DIRECTION_OUTGOING), + HistorieCommandHandler.CLIENT, "Goofy")) + .build(); + + var result = orderTranslator.isOutgoingPostfachNachrichtByMailService(nonMatchingCommand); + + assertThat(result).isFalse(); + } + } + + @DisplayName("translate order") + @Nested + class TestTranslateOrders { + @ParameterizedTest + @CsvSource(value = { "CREATE_KOMMENTAR;Kommentar", "CREATE_WIEDERVORLAGE;Wiedervorlage" }, delimiter = ';') + void shouldTranslateToCreateOrder(String target, String itemName) { + var command = orderTranslator.translateOrder(createCommand(itemName, CommandOrder.CREATE_ATTACHED_ITEM)); + + assertThat(command.getOrder().name()).isEqualTo(target); + } + + @ParameterizedTest + @CsvSource(value = { "EDIT_KOMMENTAR;Kommentar", "EDIT_WIEDERVORLAGE;Wiedervorlage" }, delimiter = ';') + void shouldTranslateToEditOrder(String target, String itemName) { + var command = orderTranslator.translateOrder(createCommand(itemName, CommandOrder.UPDATE_ATTACHED_ITEM)); + + assertThat(command.getOrder().name()).isEqualTo(target); + } + + @ParameterizedTest + @CsvSource(value = { "WIEDERVORLAGE_ERLEDIGEN;true", "WIEDERVORLAGE_WIEDEREROEFFNEN;false" }, delimiter = ';') + void shouldTranslateToWiedervolageErledigenOrWiedervolageWiedereroeffnen(String target, String doneValue) { + var command = orderTranslator.translateOrder( + createCommand(Wiedervorlage.class.getSimpleName(), CommandOrder.PATCH_ATTACHED_ITEM, Boolean.valueOf(doneValue))); + + assertThat(command.getOrder().name()).isEqualTo(target); + } + + @ParameterizedTest + @CsvSource(value = { "RECEIVE_POSTFACH_NACHRICHT;IN" }, delimiter = ';') + void shouldTranslateToReveivedIncomminNachricht(String target, String direction) { + var command = orderTranslator.translateOrder( + createCommand("MailService", CommandOrder.CREATE_ATTACHED_ITEM, direction)); + + assertThat(command.getOrder().name()).isEqualTo(target); + } + + @Test + void shouldHandleMissingDone() { + var command = orderTranslator.translateOrder(createCommand(Wiedervorlage.class.getSimpleName(), CommandOrder.PATCH_ATTACHED_ITEM)); + + assertThat(command.getOrder()).isEqualTo(CommandOrder.PATCH_ATTACHED_ITEM); + } + + @Test + void shouldHandleUnkownOrder() { + var command = orderTranslator + .translateOrder(createCommand(Wiedervorlage.class.getSimpleName(), CommandOrder.VORGANG_ABSCHLIESSEN)); + + assertThat(command.getOrder()).isEqualTo(CommandOrder.VORGANG_ABSCHLIESSEN); + } + + @Test + void shouldHandleMissingItemName() { + var command = CommandTestFactory.createBuilder().order(CommandOrder.EDIT_KOMMENTAR).body(Map.of("item", Map.of("value", "test"))).build(); + + var translatedCommand = orderTranslator.translateOrder(command); + + assertThat(translatedCommand.getOrder()).isEqualTo(CommandOrder.EDIT_KOMMENTAR); + } + + private Command createCommand(String itemName, CommandOrder order) { + return CommandTestFactory.createBuilder().order(order).body(Map.of("itemName", itemName, "item", Map.of("value", "test"))).build(); + } + + private Command createCommand(String itemName, CommandOrder order, boolean done) { + return CommandTestFactory.createBuilder().order(order).body(Map.of("itemName", itemName, "item", Map.of("done", done))) + .build(); + } + + private Command createCommand(String client, CommandOrder order, String direction) { + return CommandTestFactory.createBuilder().order(order) + .body(Map.of(HistorieCommandHandler.CLIENT, client, "itemName", PostfachMail.class.getSimpleName(), "item", + Map.of(HistorieCommandHandler.DIRECTION, direction))) + .build(); + } + + } + + @DisplayName("SEND_POSTFACH_MAIL") + @Nested + class TestSendPostfachMail { + + private Command command = CommandTestFactory.createBuilder().order(CommandOrder.SEND_POSTFACH_MAIL).build(); + + @Test + void shouldHandleUnkownOrder() { + var translatedCommand = orderTranslator.translateOrder(command); + + assertThat(translatedCommand.getOrder()).isEqualTo(CommandOrder.SEND_POSTFACH_NACHRICHT); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..760221978ea9e26f94440ad8f64162d7e5790d0b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerITCase.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +@WithMockUser +@AutoConfigureMockMvc +@SpringBootTest +class HistorieControllerITCase { + private final String PATH = HistorieController.PATH + "?vorgangId="; + + @MockBean + private CommandService service; + + @Autowired + private MockMvc mockMvc; + + @WithMockUser + @Nested + class TestGetHistorieList { + + @BeforeEach + void init() { + when(service.findFinishedCommands(any())).thenReturn(createCommandStream()); + } + + private Stream<Command> createCommandStream() { + return List.of(CommandTestFactory.create()).stream(); + } + + @Test + void shouldReturnStatusOk() throws Exception { + mockMvc.perform(get(PATH + VorgangHeaderTestFactory.ID)).andExpect(status().isOk()); + } + + @Test + void shouldContainCommands() throws Exception { + var response = mockMvc.perform(get(PATH + VorgangHeaderTestFactory.ID)).andExpect(status().isOk()); + + response.andExpect(jsonPath("$._embedded.commandList[0].order") + .value(CommandOrder.VORGANG_ANNEHMEN.name())) + .andExpect(jsonPath("$._embedded.commandList[0]").isNotEmpty()); + + } + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d135aeb36600129a6df7b847476a6d264040ba2 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieControllerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class HistorieControllerTest { + private final String PATH = HistorieController.PATH + "?vorgangId="; + + @Nested + class TestGetHistorieList { + @Spy + @InjectMocks + private HistorieController controller; + + @Mock + private HistorieService service; + + @Mock + private HistorieModelAssembler modelAssembler; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + when(service.findFinishedCommands(any())).thenReturn(createCommandStream()); + } + + private Stream<Command> createCommandStream() { + return List.of(CommandTestFactory.create()).stream(); + } + + @Test + void shouldCallService() throws Exception { + mockMvc.perform(get(PATH + VorgangHeaderTestFactory.ID)).andExpect(status().isOk()); + + verify(service).findFinishedCommands(any()); + } + + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7f4c9f11f337d6a62c4edd1c2b24bb29f4128754 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieModelAssemblerTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static de.itvsh.goofy.common.UserProfileUrlProviderTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class HistorieModelAssemblerTest { + + private static final String CREATED_BY = "createdBy"; + + @InjectMocks + private HistorieModelAssembler modelAssembler; + @Mock + private UserManagerUrlProvider userManagerUrlProvider; + + private final String COMMAND_SINGLE_PATH = "/api/histories/" + CommandTestFactory.ID; + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + @Test + void shouldHaveSelfLink() { + initUserProfileUrlProvider(urlProvider); + + var model = modelAssembler.toModel(CommandTestFactory.create()); + + assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); + } + + @DisplayName("AssignedTo Link") + @Nested + class TestAssignedToLink { + + private final String userProfileTemplateDummy = "UserProfileTemplateDummy/%s"; + + @Nested + class TesOnConfiguredUserManager { + + @Test + void shouldBePresentOnExistingValue() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(true); + when(userManagerUrlProvider.getUserProfileTemplate()).thenReturn(userProfileTemplateDummy); + + var model = modelAssembler.toModel(CommandTestFactory.createBuilder() + .body(Map.of(HistorieModelAssembler.ASSIGNED_TO_BODY_FIELD, UserProfileTestFactory.ID)).build()); + + assertThat(model.getLink(HistorieModelAssembler.REL_ASSIGNED_TO)).isPresent().get().extracting(Link::getHref) + .isEqualTo("UserProfileTemplateDummy/" + UserProfileTestFactory.ID); + } + + @Test + void shouldNotBePresentOnMissingValue() { + var model = modelAssembler.toModel(CommandTestFactory.create()); + + assertThat(model.getLink(HistorieModelAssembler.REL_ASSIGNED_TO)).isNotPresent(); + } + } + + @Nested + class TestOnNotConfiguredUserManager { + + @Test + void shouldBePresentOnExistingValue() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + + var model = modelAssembler.toModel(CommandTestFactory.createBuilder() + .body(Map.of(HistorieModelAssembler.ASSIGNED_TO_BODY_FIELD, UserProfileTestFactory.ID)).build()); + + assertThat(model.getLink(HistorieModelAssembler.REL_ASSIGNED_TO)).isNotPresent(); + } + + @Test + void shouldNotGetTemplateFromUserManagerUrlProvider() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + + modelAssembler.toModel(CommandTestFactory.createBuilder() + .body(Map.of(HistorieModelAssembler.ASSIGNED_TO_BODY_FIELD, UserProfileTestFactory.ID)).build()); + + verify(userManagerUrlProvider, never()).getUserProfileTemplate(); + } + } + } + + @DisplayName("createdBy Link") + + @Nested + class TestCreatedByLink { + @Test + void shouldExistingAtUser() { + initUserProfileUrlProvider(urlProvider); + + var model = modelAssembler.toModel(CommandTestFactory.create()); + + assertThat(model.getLink(CREATED_BY)).isPresent().get().extracting(Link::getHref) + .isEqualTo(ROOT_URL + USER_PROFILES_API_PATH + UserProfileTestFactory.ID); + } + + @Test + void shouldNotExistingAtSystemNotificationMAnager() { + var model = modelAssembler + .toModel(CommandTestFactory.createBuilder().createdBy(UserId.from(HistorieModelAssembler.SYSTEM_NOTIFICATION_MANAGER_PREFIX)) + .build()); + + assertThat(model.getLink(CREATED_BY)).isNotPresent(); + } + + @Test + void shouldNotBePresentOnNullValue() { + var model = modelAssembler.toModel(CommandTestFactory.createBuilder().createdBy(null).build()); + + assertThat(model.getLink(CREATED_BY)).isNotPresent(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4c18d0bcd9f93b1882331a7db46d8d832b4df5b5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/historie/HistorieServiceTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.historie; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class HistorieServiceTest { + + @InjectMocks + private HistorieService historieService; + @Mock + private CommandService commandService; + @Mock + private HistorieCommandHandler commandHandler; + + @DisplayName("Find finished commands") + @Nested + class TestFindFinishedCommands { + + @DisplayName("process flow") + @Nested + class TestProcessFlow { + + private Command responseCommand = CommandTestFactory.create(); + + @BeforeEach + void initMock() { + when(commandService.findFinishedCommands(any())).thenReturn(List.of(responseCommand).stream()); + when(commandHandler.isHistorieCommand(any())).thenReturn(true); + when(commandHandler.translateOrder(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCallService() { + historieService.findFinishedCommands(CommandTestFactory.VORGANG_ID).toList(); + + verify(commandService).findFinishedCommands(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldFilterInvalidCommands() { + historieService.findFinishedCommands(CommandTestFactory.VORGANG_ID).toList(); + + verify(commandHandler).isHistorieCommand(responseCommand); + } + + @Test + void shouldTranslateCommandOrderByCommandHandler() { + historieService.findFinishedCommands(CommandTestFactory.VORGANG_ID).toList(); + + verify(commandHandler).translateOrder(responseCommand); + } + } + + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarByVorgangControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarByVorgangControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6ddb92d97b80bbfab96b6a951babe0e01ba9463b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarByVorgangControllerTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.ZonedDateTime; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.kommentar.KommentarController.KommentarByVorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class KommentarByVorgangControllerTest { + + @InjectMocks + private KommentarByVorgangController controller; + + @Mock + private KommentarService service; + + @Mock + private KommentarModelAssembler modelAssembler; + + private MockMvc mockMvc; + + @BeforeEach + void initMockMvc() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetAll { + + @Test + void shouldCallEndpoint() throws Exception { + ResultActions callEndpoint = callEndpoint(); + + callEndpoint.andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).findByVorgangId(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<Kommentar>>any(), anyString()); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(KommentarByVorgangController.KOMMENTAR_BY_VORGANG_PATH + "/" + VorgangHeaderTestFactory.ID + "/kommentars")); + } + + @Test + void shouldSortByCreatedAt() { + var firstId = UUID.randomUUID().toString(); + Kommentar first = KommentarTestFactory.createBuilder().id(firstId).createdAt(ZonedDateTime.now()).build(); + + var kommentarList = controller.sortByCreatedAt(Stream.of(KommentarTestFactory.create(), first)).collect(Collectors.toList()); + + assertThat(kommentarList.get(0).getId()).isEqualTo(first.getId()); + assertThat(kommentarList.get(1).getId()).isEqualTo(KommentarTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7664e060cdec3d312752811b8e065dfafbee92b2 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandControllerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static de.itvsh.goofy.kommentar.KommentarCommandTestFactory.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.kommentar.KommentarCommandController.KommentarCommandByVorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class KommentarCommandControllerTest { + + @Captor + private ArgumentCaptor<CreateCommand> commandCaptor; + + @Nested + class TestCreateKommentarCommandByVorgang { + + @Spy + @InjectMocks + private KommentarCommandByVorgangController controller; + @Mock + private KommentarService service; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + + when(service.createKommentar(any(), any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).createKommentar(any(Kommentar.class), eq(VorgangHeaderTestFactory.ID)); + } + + @Test + void shouldReturnCreated() throws Exception { + doRequest().andExpect(status().isCreated()); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform( + post(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID) + .content(createValidRequestContent()).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()); + } + } + + @Nested + class TestCreateEditKommentarCommand { + + private final static String REPONSE_HEADER = "http://localhost/api/commands/" + KommentarCommandTestFactory.ID; + + @Spy + @InjectMocks + private KommentarCommandController controller; + @Mock + private KommentarService service; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + + when(service.editKommentar(any(), any(), anyLong())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).editKommentar(any(Kommentar.class), eq(KommentarTestFactory.ID), eq(KommentarTestFactory.VERSION)); + } + + @Test + void shouldReturnCreated() throws Exception { + doRequest().andExpect(status().isCreated()); + } + + @Test + void shouldRespondWithLocationHeader() throws Exception { + doRequest().andExpect(header().string("Location", REPONSE_HEADER)); + } + + ResultActions doRequest() throws Exception { + String content = createValidRequestContent(); + return mockMvc.perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION) + .content(content).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..46c346ff38d1bd5165f893a9d8d67d23b807bd10 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandITCase.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static de.itvsh.goofy.kommentar.KommentarCommandTestFactory.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.ValidationMessageCodes; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandRemoteService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.kommentar.KommentarCommandController.KommentarCommandByVorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class KommentarCommandITCase { + + @Autowired + private MockMvc mockMvc; + @MockBean + private KommentarRemoteService remoteService; + @MockBean + private CommandRemoteService commandRemoteService; + + @WithMockUser + @Nested + class TestCreateCommandByKommentarId { + + @BeforeEach + void initTest() { + when(remoteService.getById(any())).thenReturn(KommentarTestFactory.create()); + when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCreateCommand() throws Exception { + doRequestByKommentarId(createValidRequestContent()).andExpect(status().isCreated()); + + verify(commandRemoteService).createCommand(any()); + } + + @WithMockUser + @DisplayName("should return validation error") + @Nested + class TestValidation { + + @DisplayName("for null Text") + @Test + void createCommandWithInvalidText() throws Exception { + String content = buildContentWithText(null); + + doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("kommentar.text")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @DisplayName("for empty String in Text") + @Test + void createcommandWithShortText() throws Exception { + String content = buildContentWithText(StringUtils.EMPTY); + + doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.[0].field").value("kommentar.text")); + + } + + @DisplayName("for invalid text should have parameter") + @Test + void minMaxParameter() throws Exception { + String content = buildContentWithText(StringUtils.EMPTY); + + doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); + } + + private String buildContentWithText(String text) { + return createRequestContent(KommentarCommandTestFactory.createBuilder() + .order(CommandOrder.CREATE_ATTACHED_ITEM) + .kommentar(KommentarTestFactory.createBuilder().text(text).build()) + .build()); + } + } + + private ResultActions doRequestByKommentarId(String content) throws Exception { + return mockMvc.perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); + } + } + + @WithMockUser + @Nested + class TestCreateCommandByVorgangId { + + private static final long RELATION_ID_ON_CREATE = -1; + + @BeforeEach + void initTest() { + when(remoteService.getById(any())).thenReturn(KommentarTestFactory.create()); + when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCreateCommand() throws Exception { + doRequestByVorgangId(createValidRequestContent()).andExpect(status().isCreated()); + + verify(commandRemoteService).createCommand(any()); + } + + @WithMockUser + @DisplayName("should return validation error") + @Nested + class TestValidation { + + @DisplayName("for null Text") + @Test + void createCommandWithInvalidText() throws Exception { + String content = buildContentWithText(null); + + doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("kommentar.text")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @DisplayName("for empty String in Text") + @Test + void createcommandWithShortText() throws Exception { + String content = buildContentWithText(StringUtils.EMPTY); + + doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.[0].field").value("kommentar.text")); + + } + + @DisplayName("for invalid text should have parameter") + @Test + void minMaxParameter() throws Exception { + String content = buildContentWithText(StringUtils.EMPTY); + + doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); + } + + private String buildContentWithText(String text) { + return createRequestContent(KommentarCommandTestFactory.createBuilder() + .order(CommandOrder.CREATE_KOMMENTAR) + .kommentar(KommentarTestFactory.createBuilder().text(text).build()) + .build()); + } + } + + private ResultActions doRequestByVorgangId(String content) throws Exception { + return mockMvc.perform(post(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, + RELATION_ID_ON_CREATE) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f275416ce83addd173b6cb96031a7d356a7d5f40 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarCommandTestFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.util.Objects; + +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +public class KommentarCommandTestFactory { + + public static final String ID = CommandTestFactory.ID; + public static final CommandOrder ORDER = CommandOrder.CREATE_KOMMENTAR; + + public static KommentarCommand create() { + return createBuilder().build(); + } + + public static KommentarCommand.KommentarCommandBuilder createBuilder() { + return KommentarCommand.builder() + .id(ID) + .order(ORDER) + .kommentar(KommentarTestFactory.create()); + } + + public static String createValidRequestContent() { + return createRequestContent(create()); + } + + public static String createRequestContent(KommentarCommand command) { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithKommentar.json.tmpl", + command.getOrder().name(), + addTuedelchen(command.getKommentar().getText())); + } + + private static String addTuedelchen(String str) { + return Objects.isNull(str) ? null : String.format("\"%s\"", str); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9d439110b85b4c83fc3819b1468d015f4811645 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarControllerTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +class KommentarControllerTest { + + private final String PATH = KommentarController.KOMMENTAR_PATH + "/{id}"; + + @InjectMocks + private KommentarController controller; + + @Mock + private KommentarService service; + + @Mock + private KommentarModelAssembler modelAssembler; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetById { + + private Kommentar kommentar = KommentarTestFactory.create(); + + @BeforeEach + void mockService() { + when(service.getById(any())).thenReturn(kommentar); + } + + @Test + void shouldReturnValue() throws Exception { + ResultActions callEndpoint = callEndpoint(); + + callEndpoint.andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).getById(KommentarTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toModel(kommentar); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(PATH, KommentarTestFactory.ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..090f110faa7c9639bdcb2b52b0e47e3d3076980a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarMapperTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.vorgang.GrpcVorgangAttachedItemTestFactory; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; + +class KommentarMapperTest { + + @Spy + @InjectMocks + private KommentarMapper mapper = Mappers.getMapper(KommentarMapper.class); + @Mock + private GrpcObjectMapper grpcObjectMapper; + + @DisplayName("Map from item") + @Nested + class TestFromItem { + + @BeforeEach + void mockMapper() { + when(grpcObjectMapper.mapFromGrpc(any())).thenReturn(KommentarTestFactory.createAsMap()); + } + + @Test + void shouldCallGrpcObjectMapper() { + mapper.fromItem(GrpcVorgangAttachedItemTestFactory.create()); + + verify(grpcObjectMapper).mapFromGrpc(GrpcVorgangAttachedItemTestFactory.ITEM); + } + + @Test + void shouldCallMapperFromItemMap() { + mapper.fromItem(GrpcVorgangAttachedItemTestFactory.create()); + + verify(mapper).fromItemMap(KommentarTestFactory.createAsMap(), KommentarTestFactory.VERSION); + } + } + + @DisplayName("Map from item map") + @Nested + class TestFromItemMap { + + @Test + void shouldMapId() { + var kommentar = map(); + + assertThat(kommentar.getId()).isEqualTo(KommentarTestFactory.ID); + } + + @Test + void shouldMapVersion() { + var kommentar = map(); + + assertThat(kommentar.getVersion()).isEqualTo(KommentarTestFactory.VERSION); + } + + @Test + void shouldMapCreatedBy() { + var kommentar = map(); + + assertThat(kommentar.getCreatedBy()).isEqualTo(KommentarTestFactory.CREATED_BY); + } + + @Test + void shouldMapCreatedAt() { + var kommentar = map(); + + assertThat(kommentar.getCreatedAt()).isEqualTo(KommentarTestFactory.CREATED_AT); + } + + @Test + void shouldMapText() { + var kommentar = map(); + + assertThat(kommentar.getText()).isEqualTo(KommentarTestFactory.TEXT); + } + } + + private Kommentar map() { + return mapper.fromItemMap(KommentarTestFactory.createAsMap(), KommentarTestFactory.VERSION); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..79aa91ceb04ad3e475e410cd629cb4eba4011317 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarModelAssemblerTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static de.itvsh.goofy.common.UserProfileUrlProviderTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Collections; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.EntityLinks; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.UserProfileUrlProviderTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class KommentarModelAssemblerTest { + + private final String PATH = KommentarController.KOMMENTAR_PATH + "/"; + private static final String REL_CREATED_BY = "createdBy"; + private static final String USER_MANAGER_URL = UserProfileUrlProviderTestFactory.ROOT_URL + + UserProfileUrlProviderTestFactory.USER_PROFILES_API_PATH; + + @InjectMocks + private KommentarModelAssembler modelAssembler; + + @Mock + private EntityLinks entityLinks; + + @Nested + class TestLinksOnModel { + final String COMMAND_BY_KOMMENTAR_PATH = // + KommentarCommandController.KOMMENTAR_COMMANDS + .replace("{kommentarId}", KommentarTestFactory.ID) + .replace("{kommentarVersion}", String.valueOf(KommentarTestFactory.VERSION)); + + @Test + void shouldHaveSelfLink() { + var link = toModel().getLink(IanaLinkRelations.SELF); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(PATH + KommentarTestFactory.ID); + } + + @Test + void shouldHaveEditLink() { + var link = toModel(); + + assertThat(link.getLink(KommentarModelAssembler.REL_EDIT)).isPresent().get().extracting(Link::getHref) + .isEqualTo(COMMAND_BY_KOMMENTAR_PATH); + } + + @Test + void shouldHaveCreatedByLink() { + UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + initUserProfileUrlProvider(urlProvider); + + var link = toModel(); + + assertThat(link.getLink(REL_CREATED_BY)).isPresent().get().extracting(Link::getHref) + .isEqualTo(USER_MANAGER_URL + KommentarTestFactory.CREATED_BY); + } + + EntityModel<Kommentar> toModel() { + return modelAssembler.toModel(KommentarTestFactory.create()); + } + } + + @Nested + class TestLinksOnCollectionModel { + + @Test + void shouldHaveCreateKommentarLink() { + UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + initUserProfileUrlProvider(urlProvider); + + var collectionModel = modelAssembler.toCollectionModel(Collections.singleton(KommentarTestFactory.create()).stream(), + VorgangHeaderTestFactory.ID); + + var link = collectionModel.getLink(KommentarModelAssembler.REL_CREATE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo( + "/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/kommentarCommands"); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..290953882b05f29ac739bf50fa972f9f70f2f159 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarRemoteServiceTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.vorgang.GrpcVorgangAttachedItemTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemResponse; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItem; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemResponse; +import de.itvsh.ozg.pluto.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; + +class KommentarRemoteServiceTest { + + @Spy + @InjectMocks + private KommentarRemoteService service; + @Mock + private VorgangAttachedItemServiceBlockingStub vorgangAttachedItemServiceStub; + @Mock + private KommentarMapper mapper; + + private GrpcVorgangAttachedItem kommentar = GrpcVorgangAttachedItemTestFactory.create(); + + @DisplayName("Find by vorgangId") + @Nested + class TestFindByVorgangId { + + private GrpcFindVorgangAttachedItemRequest request = GrpcFindVorgangAttachedItemRequest.newBuilder() + .setVorgangId(VorgangHeaderTestFactory.ID) + .setItemName(KommentarRemoteService.ITEM_NAME) + .build(); + private GrpcFindVorgangAttachedItemResponse response = GrpcFindVorgangAttachedItemResponse.newBuilder() + .clearVorgangAttachedItems() + .addVorgangAttachedItems(kommentar).build(); + + @BeforeEach + void mockStub() { + when(vorgangAttachedItemServiceStub.find(any())).thenReturn(response); + } + + @Test + void shouldCallStub() { + service.findByVorgangId(VorgangHeaderTestFactory.ID); + + verify(vorgangAttachedItemServiceStub).find(request); + } + + @Test + void shouldCallMapper() { + var result = service.findByVorgangId(VorgangHeaderTestFactory.ID); + collectStreamElementsToTriggerLazyStream(result); + + verify(mapper).fromItem(any()); + } + + private void collectStreamElementsToTriggerLazyStream(Stream<Kommentar> stream) { + stream.toList(); + } + } + + @DisplayName("Find by id") + @Nested + class TestGetById { + private GrpcVorgangAttachedItemResponse response = GrpcVorgangAttachedItemResponse.newBuilder() + .setVorgangAttachedItem(GrpcVorgangAttachedItemTestFactory.create()) + .build(); + private GrpcVorgangAttachedItemRequest request = GrpcVorgangAttachedItemRequest.newBuilder() + .setId(KommentarTestFactory.ID) + .build(); + + @BeforeEach + void mockStub() { + when(vorgangAttachedItemServiceStub.getById(any())).thenReturn(response); + when(mapper.fromItem(any())).thenReturn(KommentarTestFactory.create()); + } + + @Test + void shouldCallStub() { + service.getById(KommentarTestFactory.ID); + + verify(vorgangAttachedItemServiceStub).getById(request); + } + + @Test + void shouldCallMapper() { + service.getById(KommentarTestFactory.ID); + + verify(mapper).fromItem(any()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d0d7d3f020820329e61013df0148052d5df0ee3c --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarServiceTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.attacheditem.VorgangAttachedItemService; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class KommentarServiceTest { + + @Spy + @InjectMocks + private KommentarService service; + @Mock + private KommentarRemoteService remoteService; + @Mock + private VorgangAttachedItemService vorgangAttachedItemService; + @Mock + private CurrentUserService currentUserService; + + @DisplayName("Create kommentar") + @Nested + class TestCreateKommentar { + + @DisplayName("should") + @Nested + class TestCalls { + + @BeforeEach + void mockServices() { + doReturn(KommentarTestFactory.create()).when(service).addCreated(any()); + when(vorgangAttachedItemService.createNewKommentar(any(), any())).thenReturn(CommandTestFactory.create()); + } + + @DisplayName("add created") + @Test + void shouldAddCreated() { + callCreateKommentar(); + + verify(service).addCreated(any(Kommentar.class)); + } + + @DisplayName("call vorgangattacheditem service") + @Test + void shouldCallVorgangAttachedItemService() { + callCreateKommentar(); + + verify(vorgangAttachedItemService).createNewKommentar(any(Kommentar.class), eq(VorgangHeaderTestFactory.ID)); + } + } + + @DisplayName("Add created") + @Nested + class TestAddCreated { + + @BeforeEach + void mockServices() { + when(currentUserService.getUserId()).thenReturn(UserProfileTestFactory.ID); + } + + @Test + void shouldSetCreatedAt() throws Exception { + var kommentar = callAddCreated(); + + assertThat(kommentar.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS)); + } + + @Test + void shouldSetCreatedBy() throws Exception { + var kommentar = callAddCreated(); + + assertThat(kommentar.getCreatedBy()).isEqualTo(UserProfileTestFactory.ID.toString()); + } + + private Kommentar callAddCreated() { + return service.addCreated(KommentarTestFactory.createBuilder().createdAt(null).createdBy(null).build()); + } + } + + private Command callCreateKommentar() { + return service.createKommentar(KommentarTestFactory.create(), VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Edit kommentar") + @Nested + class TestEditKommentar { + + @Captor + private ArgumentCaptor<Kommentar> kommentarCaptor; + + @BeforeEach + void mockKommentarService() { + when(remoteService.getById(anyString())).thenReturn(KommentarTestFactory.createBuilder().text("text needs to be override").build()); + } + + @Test + void shouldCallGetById() { + callEditKommentar(); + + verify(service).getById(KommentarTestFactory.ID); + } + + @Test + void shouldReplaceText() { + callEditKommentar(); + + verify(vorgangAttachedItemService).editKommentar(kommentarCaptor.capture(), eq(KommentarTestFactory.ID), + eq(KommentarTestFactory.VERSION)); + assertThat(kommentarCaptor.getValue().getText()).isEqualTo(KommentarTestFactory.TEXT); + } + + @Test + void shouldCallVorgangAttachedItemService() { + callEditKommentar(); + + verify(vorgangAttachedItemService).editKommentar(any(Kommentar.class), eq(KommentarTestFactory.ID), eq(KommentarTestFactory.VERSION)); + } + + private Command callEditKommentar() { + return service.editKommentar(KommentarTestFactory.create(), KommentarTestFactory.ID, KommentarTestFactory.VERSION); + } + } + + @Nested + class TestGetById { + + @Test + void shouldCallRemoteService() { + service.getById(KommentarTestFactory.ID); + + verify(remoteService).getById(KommentarTestFactory.ID); + } + } + + @Nested + class TestFindByVorgangId { + + @Test + void shouldCallRemoteService() { + service.findByVorgangId(VorgangHeaderTestFactory.ID); + + verify(remoteService).findByVorgangId(VorgangHeaderTestFactory.ID); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7fb92bf4d98526faf1833bc3185f91e82e399ce3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/kommentar/KommentarTestFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.kommentar; + +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.UUID; + +import com.thedeanda.lorem.Lorem; +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +public class KommentarTestFactory { + + private static Lorem lorem = LoremIpsum.getInstance(); + + public static final String ID = UUID.randomUUID().toString(); + public static final long VERSION = 73; + + public static final String CREATED_BY = UserProfileTestFactory.ID.toString(); + public static final String CREATED_AT_STR = "2021-01-10T10:30:00Z"; + public static final ZonedDateTime CREATED_AT = ZonedDateTime.parse(CREATED_AT_STR); + + public static final String TEXT = lorem.getWords(15); + + public static Kommentar create() { + return createBuilder().build(); + } + + public static Kommentar.KommentarBuilder createBuilder() { + return Kommentar.builder() + .id(ID) + .vorgangId(VorgangHeaderTestFactory.ID) + .version(VERSION) + .text(TEXT) + .createdBy(CREATED_BY) + .createdAt(CREATED_AT); + } + + public static Map<String, Object> createAsMap() { + return Map.of( + KommentarMapper.ID, ID, + KommentarMapper.TEXT, TEXT, + KommentarMapper.CREATED_BY, CREATED_BY, + KommentarMapper.CREATED_AT, CREATED_AT_STR); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/GrpcPostfachMailTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/GrpcPostfachMailTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..62f52df85d5fa33e4864d422f70768df92bafc14 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/GrpcPostfachMailTestFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static de.itvsh.goofy.postfach.PostfachMailTestFactory.*; + +import java.util.Arrays; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.ozg.mail.postfach.GrpcDirection; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailResponse; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailsResponse; +import de.itvsh.ozg.mail.postfach.GrpcPostfachMail; + +public class GrpcPostfachMailTestFactory { + + public static GrpcPostfachMail create() { + return createBuilder().build(); + } + + public static GrpcPostfachMail.Builder createBuilder() { + return GrpcPostfachMail.newBuilder() + .setId(ID) + .setVorgangId(VORGANG_ID) + .setPostfachId(POSTFACH_ID) + .setCreatedAt(CREATED_AT_STR) + .setCreatedBy(CREATED_BY.toString()) + .setSentAt(SENT_AT_STR) + .setSentSuccessful(SENT_SUCCESSFUL) + .setMessageCode(MESSAGE_CODE) + .setDirection(GrpcDirection.OUT) + .setReplyOption(REPLY_OPTION.name()) + .setSubject(SUBJECT) + .setMailBody(MAIL_BODY) + .addAttachment(BinaryFileTestFactory.ID); + } + + public static GrpcFindPostfachMailsResponse createFindPostfachMailsResponse() { + return GrpcFindPostfachMailsResponse.newBuilder() + .addAllMails(Arrays.asList(create())) + .build(); + } + + public static GrpcFindPostfachMailResponse createFindPostfachMailResponse() { + return GrpcFindPostfachMailResponse.newBuilder() + .setNachricht(create()) + .build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailCommandControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailCommandControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..99507542223557c88e854baad788598e9357eb81 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailCommandControllerTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.postfach.PostfachMailController.PostfachMailCommandController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class PostfachMailCommandControllerTest { + + @InjectMocks + private PostfachMailCommandController controller; + @Mock + private PostfachMailService service; + @Mock + private CommandByRelationController commandByRelationController; + + private MockMvc mockMvc; + + @BeforeEach + void initMockMvc() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestCreateCommand { + + @Captor + private ArgumentCaptor<CreateCommand> createCommandCaptor; + + @BeforeEach + void mockController() { + when(commandByRelationController.createCommand(any(CreateCommand.class))).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCallCommandByRelationController() throws Exception { + doRequest(); + + verify(commandByRelationController).createCommand(createCommandCaptor.capture()); + } + + @Test + void shouldCallServiceResend() throws Exception { + doRequest(); + + verify(service).resendPostfachMail(CommandTestFactory.ID, PostfachMailTestFactory.ID); + } + + @Test + void shouldReturnResponse() throws Exception { + doRequest().andExpect(status().isCreated()); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(post("/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/postfachMails/" + PostfachMailTestFactory.ID + "/commands") + .contentType(MediaType.APPLICATION_JSON) + .content(PostfachMailTestFactory.buildResendPostfachMailContent())); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..99cd81c922032994f8984c9ff2867b22850cf7d1 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailControllerTest.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.Optional; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.vorgang.Antragsteller; +import de.itvsh.goofy.vorgang.EingangTestFactory; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import lombok.SneakyThrows; + +class PostfachMailControllerTest { + + @Spy + @InjectMocks // NOSONAR + private PostfachMailController controller; + @Mock + private PostfachMailService service; + @Mock + private PostfachMailModelAssembler assembler; + @Mock + private VorgangController vorgangController; + @Mock + private BinaryFileController binaryFileController; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @DisplayName("Get all") + @Nested + class TestGetAll { + + @BeforeEach + void mockVorgangController() { + when(vorgangController.getVorgang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).getAll(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallVorgangController() throws Exception { + doRequest(); + + verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + doRequest(); + + verify(assembler).toCollectionModel(ArgumentMatchers.<Stream<PostfachMail>>any(), any(), + ArgumentMatchers.<Optional<String>>any()); + } + + private void doRequest() throws Exception { + mockMvc.perform(get(PostfachMailController.PATH + "?" + PostfachMailController.PARAM_VORGANG_ID + "=" + VorgangHeaderTestFactory.ID)) + .andExpect(status().isOk()); + } + } + + @DisplayName("Get all as pdf") + @Nested + class TestGetAllAsPdf { + + @Mock + private StreamingResponseBody streamingBody; + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @BeforeEach + void mockService() { + when(vorgangController.getVorgang(anyString())).thenReturn(vorgang); + } + + @Test + void shouldGetVorgang() { + doRequest(); + + verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID); + } + + @Disabled("FIXME: Kippt um, wenn man alles Tests ausfuehrt") + @Test + void shouldCallService() { + doRequest(); + + verify(service).getAllAsPdf(eq(vorgang), any(OutputStream.class)); + } + + @Test + void shouldBuildResponseEntity() { + doReturn(streamingBody).when(controller).createDownloadStreamingBody(vorgang); + + doRequest(); + + verify(controller).buildResponseEntity(vorgang, streamingBody); + } + + @SneakyThrows + @Test + void shouldReturnResponse() { + doReturn(streamingBody).when(controller).createDownloadStreamingBody(vorgang); + + doRequest() + .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, + String.format(PostfachMailController.PDF_NAME_TEMPLATE, VorgangHeaderTestFactory.NUMMER, + PostfachMailController.PDF_NAME_DATE_FORMATTER.format(new Date())))) + .andExpect(content().contentType(MediaType.APPLICATION_PDF)); + } + + @SneakyThrows + private ResultActions doRequest() { + return mockMvc + .perform(get(PostfachMailController.PATH + "?" + PostfachMailController.PARAM_VORGANG_ID + "=" + VorgangHeaderTestFactory.ID) + .accept(MediaType.APPLICATION_PDF_VALUE)) + .andExpect(status().isOk()); + } + } + + @Nested + class TestCreateDownloadStreamingBody { + + @Mock + private OutputStream out; + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @Test + void shouldCallServiceGetAllAsPdf() throws IOException { + controller.createDownloadStreamingBody(vorgang).writeTo(out); + + verify(service).getAllAsPdf(eq(vorgang), any()); + } + } + + @DisplayName("Get postfachId") + @Nested + class TestGetPostfachId { + + @DisplayName("without antragsteller") + @Nested + class TestWithoutAntragsteller { + + @Test + void shouldReturnEmptyOptional() { + var result = controller.getPostfachId(buildVorgangWithoutAntragsteller()); + + assertThat(result).isNotPresent(); + } + + private VorgangWithEingang buildVorgangWithoutAntragsteller() { + return VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()).build(); + } + } + + @DisplayName("with empty postfachId") + @Nested + class TestWithEmptyPostfachId { + + @DisplayName("should proceed with empty postfachId") + @Test + void shouldReturnEmptyOptional() { + var result = controller.getPostfachId(buildVorgangWithEmptyPostfachId()); + + assertThat(result).isNotPresent(); + } + + private VorgangWithEingang buildVorgangWithEmptyPostfachId() { + return VorgangWithEingangTestFactory.createBuilder() + .eingang(EingangTestFactory.createBuilder() + .antragsteller(Antragsteller.builder().postfachId(StringUtils.EMPTY).build()) + .build()) + .build(); + } + } + } + + @DisplayName("Find postfach attachments") + @Nested + class TestFindPostfachAttachments { + + @BeforeEach + void init() { + when(service.findById(any())).thenReturn(PostfachMailTestFactory.create()); + } + + @Test + void shouldReturnSuccess() throws Exception { + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).findById(PostfachNachrichtId.from(PostfachMailTestFactory.ID)); + } + + @Test + void shouldCallFileController() throws Exception { + doRequest(); + + verify(binaryFileController).getFiles(PostfachMailTestFactory.ATTACHMENTS); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(PostfachMailController.PATH + "/{nachrichtId}/attachments", PostfachMailTestFactory.ID)); + } + } + + @DisplayName("Is postfach configured") + @Nested + class TestIsPostfachConfigured { + + @Test + void shouldCallService() { + controller.isPostfachConfigured(); + + verify(service).isPostfachConfigured(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..1c88d94109514117d63bc98f563ea33b5a14e344 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailITCase.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.util.ReflectionTestUtils; + +import de.itvsh.goofy.CallScope; +import de.itvsh.goofy.vorgang.EingangTestFactory; +import de.itvsh.goofy.vorgang.VorgangController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.ozg.mail.postfach.PostfachServiceGrpc.PostfachServiceBlockingStub; + +@SpringBootTest +public class PostfachMailITCase { + + @Autowired + private PostfachMailController controller; + + @SpyBean + private PostfachMailRemoteService postfachRemoteService; + @Mock + private PostfachServiceBlockingStub serviceStub; + @MockBean + private VorgangController vorgangController; + + @Autowired + private CallScope callScope; + + @BeforeEach + void initTest() { + ReflectionTestUtils.setField(postfachRemoteService, "serviceStub", serviceStub); + + callScope.startScope(); + } + + @Nested + @WithMockUser + class TestGetAll { + + @BeforeEach + void mock() { + when(serviceStub.findPostfachMails(any())).thenReturn(GrpcPostfachMailTestFactory.createFindPostfachMailsResponse()); + when(vorgangController.getVorgang(anyString())).thenReturn( + VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()).build()); + } + + @Test + void shouldProceedWithoutAntragsteller() { + controller.getAll(VorgangHeaderTestFactory.ID); + + verify(vorgangController).getVorgang(VorgangHeaderTestFactory.ID); + verify(postfachRemoteService).findPostfachMails(VorgangHeaderTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b95f46d6beed27e99ca8fc062d45c7744b7d9406 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailMapperTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.TimeMapper; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.ozg.mail.postfach.GrpcPostfachMail; + +class PostfachMailMapperTest { + + @InjectMocks // NOSONAR + private PostfachMailMapper mapper = Mappers.getMapper(PostfachMailMapper.class); + @Spy + private UserIdMapper userIdMapper = Mappers.getMapper(UserIdMapper.class); + + @Mock + private TimeMapper timeMapper; + + @Nested + class TestGrpcPostfachMailToPostfachMail { + + @Test + void shouldMap() { + when(timeMapper.parseString(PostfachMailTestFactory.CREATED_AT_STR)).thenReturn(PostfachMailTestFactory.CREATED_AT); + when(timeMapper.parseString(PostfachMailTestFactory.SENT_AT_STR)).thenReturn(PostfachMailTestFactory.SENT_AT); + + GrpcPostfachMail grpcPostfachMail = GrpcPostfachMailTestFactory.create(); + + var postfachMail = mapper.toPostfachMail(grpcPostfachMail); + + verify(timeMapper, times(2)).parseString(anyString()); + assertThat(postfachMail).isNotNull().usingRecursiveComparison().isEqualTo(PostfachMailTestFactory.create()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1214f6a74d39fb64a904f51852fd8f9f3cd27951 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailModelAssemblerTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; + +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserManagerUrlProvider; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; + +class PostfachMailModelAssemblerTest { + + private static final String ROOT_URL = "http://localhost:9191"; + private static final String USER_PROFILES_API_PATH = "/api/usersProfiles/"; + + @InjectMocks + private PostfachMailModelAssembler modelAssembler; + @Mock + private UserManagerUrlProvider userManagerUrlProvider; + + @BeforeEach + void mock() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + } + + @Nested + class TestLinksOnModel { + + @Test + void shouldHaveSelfLink() { + var link = modelAssembler.toModel(PostfachMailTestFactory.create()).getLink(IanaLinkRelations.SELF_VALUE); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo(PostfachMailController.PATH + "/" + PostfachMailTestFactory.ID); + } + + @Nested + class ResendLink { + + @DisplayName("if 'sentSuccessful' is false, add link") + @Test + void shouldHaveResendLink() { + var link = modelAssembler.toModel(PostfachMailTestFactory.createBuilder().sentSuccessful(false).build()) + .getLink(PostfachMailModelAssembler.REL_RESEND_POSTFACH_MAIL); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo( + "/api/vorgangs/" + PostfachMailTestFactory.VORGANG_ID + "/postfachMails/" + PostfachMailTestFactory.ID + "/commands"); + } + + @DisplayName("if 'sentSuccessful' is true, hide link") + @Test + void shouldNotHaveResendLink() { + var link = modelAssembler.toModel(PostfachMailTestFactory.createBuilder().sentSuccessful(true).build()) + .getLink(PostfachMailModelAssembler.REL_RESEND_POSTFACH_MAIL); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class ResetHasNewPostfachNachricht { + Stream<PostfachMail> mails = List.of(PostfachMailTestFactory.create()).stream(); + + @Test + void shouldHaveLink() { + var link = modelAssembler.toCollectionModel(mails, VorgangWithEingangTestFactory.create(), Optional.of(PostfachMailTestFactory.ID)) + .getLink(PostfachMailModelAssembler.REL_RESET_NEW_POSTFACH_MAIL); + + assertThat(link).isPresent(); + } + + @Test + void shouldNotNaveLink() { + var link = modelAssembler + .toCollectionModel(mails, VorgangWithEingangTestFactory.createBuilder().hasNewPostfachNachricht(false).build(), + Optional.of(PostfachMailTestFactory.ID)) + .getLink(PostfachMailModelAssembler.REL_RESET_NEW_POSTFACH_MAIL); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class ToAttachments { + + @Test + @DisplayName("should be present if attachments present") + void shouldBePresent() { + var link = modelAssembler.toModel(PostfachMailTestFactory.create()).getLink(PostfachMailModelAssembler.REL_ATTACHMENTS); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/postfachMails/" + PostfachMailTestFactory.ID + "/attachments"); + } + + @Test + @DisplayName("should NOT be present if there are no attachments") + void shouldNOTBePresent() { + var link = modelAssembler.toModel(PostfachMailTestFactory.createBuilder().clearAttachments().build()) + .getLink(PostfachMailModelAssembler.REL_ATTACHMENTS); + + assertThat(link).isNotPresent(); + } + } + + @DisplayName("created by link") + @Nested + class CreatedByLink { + + @Nested + class TestOnConfiguredUserManager { + + private final String userProfileTemplate = "UserProfileTemplateDummy/%s"; + + @BeforeEach + void mock() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(true); + } + + @Test + void shouldBePresent() { + when(userManagerUrlProvider.getUserProfileTemplate()).thenReturn(userProfileTemplate); + + var link = modelAssembler.toModel(PostfachMailTestFactory.create()).getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo("UserProfileTemplateDummy/" + PostfachMailTestFactory.CREATED_BY); + } + + @DisplayName("should not be present if the value of createdBy starts with 'system'") + @Test + void shouldNOTBePresentOnSystemUser() { + var link = modelAssembler + .toModel(PostfachMailTestFactory.createBuilder() + .createdBy(UserId.from(PostfachMailModelAssembler.SYSTEM_USER_IDENTIFIER + UUID.randomUUID().toString())).build()) + .getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + assertThat(link).isNotPresent(); + } + + @Test + void shouldNotBePresentOnNull() { + var link = modelAssembler.toModel(PostfachMailTestFactory.createBuilder().createdBy(null).build()) + .getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class TestOnNonConfiguredUserManager { + + @BeforeEach + void mock() { + when(userManagerUrlProvider.isConfiguredForUserProfile()).thenReturn(false); + } + + @Test + void shouldNOTBePresentOnNotConfiguredUserManager() { + var link = modelAssembler + .toModel(PostfachMailTestFactory.createBuilder() + .createdBy(UserId.from(PostfachMailModelAssembler.SYSTEM_USER_IDENTIFIER + UUID.randomUUID().toString())).build()) + .getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + assertThat(link).isNotPresent(); + } + + @Test + void shouldNotCallUserManagerUrlProvider() { + modelAssembler + .toModel(PostfachMailTestFactory.createBuilder() + .createdBy(UserId.from(PostfachMailModelAssembler.SYSTEM_USER_IDENTIFIER + UUID.randomUUID().toString())).build()) + .getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + verify(userManagerUrlProvider, never()).getUserProfileTemplate(); + } + + @Test + void shouldNotBePresentOnNull() { + var link = modelAssembler.toModel(PostfachMailTestFactory.createBuilder().createdBy(null).build()) + .getLink(PostfachMailModelAssembler.REL_CREATED_BY); + + assertThat(link).isNotPresent(); + } + } + } + } + + @Nested + class TestLinksOnCollectionModel { + + @Test + void shouldHaveSelfLink() { + var link = modelAssembler + .toCollectionModel(Stream.of(PostfachMailTestFactory.create()), VorgangWithEingangTestFactory.create(), + Optional.empty()) + .getLink(IanaLinkRelations.SELF_VALUE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(PostfachMailController.PATH); + } + + @Nested + class SendPostfachMailLink { + + private final String LINKREL = PostfachMailModelAssembler.REL_SEND_POSTFACH_MAIL; + + @Test + void shouldHaveLink() { + var link = toCollectionModel(Optional.of(PostfachMailTestFactory.POSTFACH_ID)).getLink(LINKREL); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()) + .isEqualTo(String.format("/api/vorgangs/%s/relations/%s/%s/commands", + VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION)); + } + + @Test + void shouldNotHaveLink() { + var link = toCollectionModel(Optional.empty()).getLink(LINKREL); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class UploadAttachmentLink { + + private final String LINKREL = PostfachMailModelAssembler.REL_UPLOAD_ATTACHMENT; + + @Test + void shouldHaveLink() { + var link = toCollectionModel(Optional.of(PostfachMailTestFactory.POSTFACH_ID)).getLink(LINKREL); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()) + .isEqualTo(String.format("/api/binaryFiles/%s/postfachNachrichtAttachment/file", VorgangHeaderTestFactory.ID)); + } + + @Test + void shouldNotHaveLink() { + var link = toCollectionModel(Optional.empty()).getLink(LINKREL); + + assertThat(link).isNotPresent(); + } + + } + + private CollectionModel<EntityModel<PostfachMail>> toCollectionModel(Optional<String> postfachId) { + return modelAssembler.toCollectionModel(Stream.of(PostfachMailTestFactory.create()), VorgangWithEingangTestFactory.create(), + postfachId); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5926a4d7f6d3e20292d867239a78414d741a5498 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailRemoteServiceTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailRequest; +import de.itvsh.ozg.mail.postfach.GrpcFindPostfachMailsRequest; +import de.itvsh.ozg.mail.postfach.GrpcIsPostfachConfiguredRequest; +import de.itvsh.ozg.mail.postfach.GrpcIsPostfachConfiguredResponse; +import de.itvsh.ozg.mail.postfach.GrpcPostfachMail; +import de.itvsh.ozg.mail.postfach.GrpcResendPostfachMailRequest; +import de.itvsh.ozg.mail.postfach.PostfachServiceGrpc.PostfachServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; + +class PostfachMailRemoteServiceTest { + + @InjectMocks // NOSONAR + private PostfachMailRemoteService service; + @Mock + private PostfachServiceBlockingStub serviceStub; + @Mock + private PostfachMailMapper postfachNachrichtMapper; + @Mock + private ContextService contextService; + + private GrpcCallContext callContext = GrpcCallContext.newBuilder().build(); + + @BeforeEach + void initMocks() { + when(contextService.createCallContext()).thenReturn(callContext); + } + + @Nested + class TestFindPostfachMail { + + @Captor + private ArgumentCaptor<GrpcFindPostfachMailRequest> requestCaptor; + + @BeforeEach + void init() { + when(serviceStub.findPostfachMail(any())).thenReturn(GrpcPostfachMailTestFactory.createFindPostfachMailResponse()); + } + + @Test + void shouldCallStub() { + service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + verify(serviceStub).findPostfachMail(any(GrpcFindPostfachMailRequest.class)); + } + + @Test + void shouldCallCallContextService() { + service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + verify(contextService).createCallContext(); + } + + @Test + void shouldContainNachrichtId() { + service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + verify(serviceStub).findPostfachMail(requestCaptor.capture()); + + assertThat(requestCaptor.getValue().getNachrichtId()).isEqualTo(PostfachMailTestFactory.ID); + } + + @Test + void shouldCallMapper() { + service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + verify(postfachNachrichtMapper).toPostfachMail(any()); + } + } + + @Nested + class TestFindPostfachMails { + + @BeforeEach + void mockServiceStub() { + when(serviceStub.findPostfachMails(any())).thenReturn(GrpcPostfachMailTestFactory.createFindPostfachMailsResponse()); + } + + @Test + void shouldCallStub() { + service.findPostfachMails(VorgangHeaderTestFactory.ID); + + verify(serviceStub).findPostfachMails(any(GrpcFindPostfachMailsRequest.class)); + } + + @Test + void shouldCallCallContextService() { + service.findPostfachMails(VorgangHeaderTestFactory.ID); + + verify(contextService).createCallContext(); + } + + @Test + void shouldCallMapper() { + var result = service.findPostfachMails(VorgangHeaderTestFactory.ID); + collectStreamElementsToTriggerLazyStream(result); + + verify(postfachNachrichtMapper).toPostfachMail(any(GrpcPostfachMail.class)); + } + + void collectStreamElementsToTriggerLazyStream(Stream<PostfachMail> stream) { + stream.collect(Collectors.toList()); + } + } + + @Nested + class TestResendPostfachMail { + + @Test + void shouldCallStub() { + service.resendPostfachMail(CommandTestFactory.ID, PostfachMailTestFactory.ID); + + verify(serviceStub).resendPostfachMail(any(GrpcResendPostfachMailRequest.class)); + } + + @Test + void shouldCallContextService() { + service.resendPostfachMail(CommandTestFactory.ID, PostfachMailTestFactory.ID); + + verify(contextService).createCallContext(); + } + } + + @Nested + class TestIsPostfachConfigured { + + @BeforeEach + void mockStub() { + when(serviceStub.isPostfachConfigured(any(GrpcIsPostfachConfiguredRequest.class))) + .thenReturn(GrpcIsPostfachConfiguredResponse.newBuilder().setIsConfigured(true).build()); + } + + @Test + void shouldCallServiceStub() { + service.isPostfachConfigured(); + + verify(serviceStub).isPostfachConfigured(any(GrpcIsPostfachConfiguredRequest.class)); + } + + @Test + void shouldCallContextService() { + service.isPostfachConfigured(); + + verify(contextService).createCallContext(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5fee86fc5c2f088e5acec53f45c3ee9cc976db71 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailServiceTest.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.binaryfile.BinaryFileService; +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.errorhandling.ResourceNotFoundException; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserProfile; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserService; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; + +class PostfachMailServiceTest { + + @Spy + @InjectMocks // NOSONAR + private PostfachMailService service; + @Mock + private PostfachNachrichtPdfService pdfService; + @Mock + private PostfachMailRemoteService remoteService; + @Mock + private BinaryFileService fileService; + @Mock + private UserService userService; + + @DisplayName("Get all") + @Nested + class TestGetAll { + + @Test + void shouldCallRemoteService() { + service.getAll(VorgangHeaderTestFactory.ID); + + verify(remoteService).findPostfachMails(VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Find by id") + @Nested + class TestFindById { + + private PostfachMail mail = PostfachMailTestFactory.create(); + + @BeforeEach + void init() { + when(remoteService.findById(any())).thenReturn(Optional.of(mail)); + } + + @Test + void shouldCallRemoteService() { + service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + verify(remoteService).findById(PostfachMailTestFactory.NACHRICHT_ID); + } + + @Test + void shouldThrowExceptionIfNotFound() { + when(remoteService.findById(any())).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, () -> service.findById(PostfachMailTestFactory.NACHRICHT_ID)); + } + + @Test + void shouldReturnNachricht() { + var result = service.findById(PostfachMailTestFactory.NACHRICHT_ID); + + assertThat(result).isSameAs(mail); + } + } + + @DisplayName("Resend postfach mail") + @Nested + class TestResendPostfachMail { + + @Test + void shouldCallRemoteService() { + service.resendPostfachMail(CommandTestFactory.ID, PostfachMailTestFactory.ID); + + verify(remoteService).resendPostfachMail(CommandTestFactory.ID, PostfachMailTestFactory.ID); + } + } + + @DisplayName("Is postfach configured") + @Nested + class TestIsPostfachConfigured { + + @Test + void shouldCallRemoteServiceOnMissingProperty() { + service.isPostfachConfigured(); + + verify(remoteService).isPostfachConfigured(); + } + + @Test + void shouldNotCallRemoteServiceOnExistingProperty() throws Exception { + setIsConfigured(); + + service.isPostfachConfigured(); + + verifyNoInteractions(remoteService); + } + + private void setIsConfigured() throws Exception { + var isPostfachConfigured = PostfachMailService.class.getDeclaredField("isPostfachConfigured"); + isPostfachConfigured.setAccessible(true); + isPostfachConfigured.set(service, true); + } + } + + @DisplayName("Get all as pdf") + @Nested + class TestGetAllAsPdf { + + @Mock + private OutputStream outputStream; + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + private final Stream<PostfachNachrichtPdfData> postfachNachrichtPdfModelDataList = Stream + .of(PostfachNachrichtPdfData.builder().build());// TODO TestFactory erstellen + + @DisplayName("should call") + @Nested + class TestCall { + + @BeforeEach + void mock() { + doReturn(postfachNachrichtPdfModelDataList).when(service).buildPostfachNachrichtPdfDataList(anyString()); + } + + @Test + void buildPostfachNachrichtPdfModelDataList() { + service.getAllAsPdf(vorgang, outputStream); + + verify(service).buildPostfachNachrichtPdfDataList(VorgangHeaderTestFactory.ID); + } + + @Test + void pdfService() { + service.getAllAsPdf(vorgang, outputStream); + + verify(pdfService).getAllAsPdf(vorgang, postfachNachrichtPdfModelDataList, outputStream); + } + } + + @DisplayName("build postfach nachrichten pdf data") + @Nested + class TestBuildPostfachNachrichtPdfDataList { + + private final PostfachMail postfachNachricht = PostfachMailTestFactory.create(); + + @DisplayName("should call") + @Nested + class TestCall { + + private final Stream<PostfachMail> postfachMail = Stream.of(postfachNachricht); + private final Stream<OzgFile> ozgFile = Stream.of(OzgFileTestFactory.create()); + + @BeforeEach + void mock() { + when(remoteService.findPostfachMails(anyString())).thenReturn(postfachMail); + when(fileService.getFiles(anyList())).thenReturn(ozgFile); + } + + @Test + void shouldCallRemoteService() { + service.buildPostfachNachrichtPdfDataList(VorgangHeaderTestFactory.ID); + + verify(remoteService).findPostfachMails(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallBinaryFileService() { + service.buildPostfachNachrichtPdfDataList(VorgangHeaderTestFactory.ID); + + verify(fileService).getFiles(List.of(BinaryFileTestFactory.FILE_ID)); + } + + @Test + void shouldCallBuildPostfachNachrichtPdfData() { + doReturn(PostfachNachrichtPdfData.builder().build()).when(service).buildPostfachNachrichtPdfData(any(), any()); + + service.buildPostfachNachrichtPdfDataList(VorgangHeaderTestFactory.ID).toList(); + + verify(service).buildPostfachNachrichtPdfData(postfachNachricht, Map.of(OzgFileTestFactory.ID, OzgFileTestFactory.NAME)); + } + } + + @DisplayName("for single postfachNachricht") + @Nested + class TestBuildPostfachNachrichtPdfData { + + private final UserProfile user = UserProfileTestFactory.create(); + + @BeforeEach + void mock() { + when(userService.getById(any(UserId.class))).thenReturn(user); + } + + @Test + void shouldCallUserService() { + buildPostfachNachrichtPdfData(); + + verify(userService).getById(UserProfileTestFactory.ID); + } + + @Test + void shouldHaveSetCreatedAt() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getCreatedAt()).isEqualTo(PostfachMailTestFactory.CREATED_AT); + } + + @Test + void shouldHaveSetCreatedByName() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getUser()).isEqualTo(user); + } + + @Test + void shouldHaveSetSubject() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getSubject()).isEqualTo(PostfachMailTestFactory.SUBJECT); + } + + @Test + void shouldHaveSetMailBody() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getMailBody()).isEqualTo(PostfachMailTestFactory.MAIL_BODY); + } + + @Test + void shouldHaveSetAttachmentNames() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getAttachmentNames()).isEqualTo(List.of(OzgFileTestFactory.NAME)); + } + } + + @DisplayName("user") + @Nested + class TestUser { + + @DisplayName("exists") + @Nested + class TestOnNonNull { + + private final UserProfile user = UserProfileTestFactory.create(); + + @BeforeEach + void mock() { + when(userService.getById(any(UserId.class))).thenReturn(user); + } + + @Test + void shouldCallUserService() { + buildPostfachNachrichtPdfData(); + + verify(userService).getById(UserProfileTestFactory.ID); + } + + @Test + void shouldHaveSetCreatedByName() { + var postfachNachricht = buildPostfachNachrichtPdfData(); + + assertThat(postfachNachricht.getUser()).isEqualTo(user); + } + } + + @DisplayName("not exists") + @Nested + class TestOnNull { + + private final PostfachMail postfachNachrichtWithoutCreatedBy = PostfachMailTestFactory.createBuilder().createdBy(null) + .build(); + + @Test + void shouldNotCallUserService() { + buildPostfachNachrichtPdfDataWithoutUser(); + + verify(userService, never()).getById(any()); + } + + @Test + void shouldHaveSetNullAsUser() { + var postfachNachricht = buildPostfachNachrichtPdfDataWithoutUser(); + + assertThat(postfachNachricht.getUser()).isNull(); + } + + private PostfachNachrichtPdfData buildPostfachNachrichtPdfDataWithoutUser() { + return service.buildPostfachNachrichtPdfData(postfachNachrichtWithoutCreatedBy, + Map.of(BinaryFileTestFactory.FILE_ID, OzgFileTestFactory.NAME)); + } + } + } + + private PostfachNachrichtPdfData buildPostfachNachrichtPdfData() { + return service.buildPostfachNachrichtPdfData(postfachNachricht, Map.of(BinaryFileTestFactory.FILE_ID, OzgFileTestFactory.NAME)); + } + } + + @DisplayName("get file ids from all attachments") + @Nested + class TestGetFileIdsFromAllAttachments { + + @Test + void shouldReturnFileIdsFromPostfachMail() { + var fileIds = service.getFileIdsFromAllAttachments(Stream.of(PostfachMailTestFactory.create())); + + assertThat(fileIds).containsExactly(BinaryFileTestFactory.FILE_ID); + } + + @Test + void shouldReturnEmptyListOnNoAttachments() { + var fileIds = service.getFileIdsFromAllAttachments(Stream.of(PostfachMailTestFactory.createBuilder().clearAttachments().build())); + + assertThat(fileIds).isEmpty(); + } + } + + @DisplayName("get User") + @Nested + class TestGetUser { + + @DisplayName("on existing client user") + @Nested + class TestOnExistingClientUser { + + @Test + void shouldCallUserServiceOnExistingUser() { + service.getUser(PostfachMailTestFactory.create()); + + verify(userService).getById(UserProfileTestFactory.ID); + } + + @Test + void shouldReturnUserProfileWithId() { + when(userService.getById(any())).thenReturn(UserProfileTestFactory.create()); + + var user = service.getUser(PostfachMailTestFactory.create()); + + assertThat(user.getId()).isEqualTo(UserProfileTestFactory.ID); + } + } + + @DisplayName("on existing system user") + @Nested + class TestOnSystemUser { + + private final PostfachMail postfachMail = PostfachMailTestFactory.createBuilder() + .createdBy(UserId.from(UserProfileTestFactory.SYSTEM_USER)).build(); + + @Test + void shouldNotCallUserService() { + service.getUser(postfachMail); + + verify(userService, never()).getById(UserProfileTestFactory.ID); + } + + @Test + void shouldReturnUserProfileWithId() { + var user = service.getUser(postfachMail); + + assertThat(user.getId()).hasToString(UserProfileTestFactory.SYSTEM_USER); + } + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..067f3f49766abfaf915b037a3d02dd7a77f3c07e --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachMailTestFactory.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang3.RandomStringUtils; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.postfach.PostfachMail.Direction; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +public class PostfachMailTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final PostfachNachrichtId NACHRICHT_ID = PostfachNachrichtId.from(ID); + public static final String VORGANG_ID = VorgangHeaderTestFactory.ID; + public static final String POSTFACH_ID = UUID.randomUUID().toString(); + public static final String CREATED_AT_STR = "2000-01-01T01:00:00Z"; + public static final ZonedDateTime CREATED_AT = ZonedDateTime.parse(CREATED_AT_STR); + public static final UserId CREATED_BY = UserProfileTestFactory.ID; + public static final Direction DIRECTION = Direction.OUT; + public static final String RECEIVER = LoremIpsum.getInstance().getEmail(); + public static final String SUBJECT = RandomStringUtils.randomAlphanumeric(70); + public static final String MAIL_BODY = LoremIpsum.getInstance().getParagraphs(1, 1); + public static final ReplyOption REPLY_OPTION = ReplyOption.POSSIBLE; + static final boolean SENT_SUCCESSFUL = true; + public static final String SENT_AT_STR = "2000-01-01T10:00:00Z"; + public static final ZonedDateTime SENT_AT = ZonedDateTime.parse(SENT_AT_STR); + public static final String MESSAGE_CODE = LoremIpsum.getInstance().getWords(3).concat("."); + + public static final List<FileId> ATTACHMENTS = List.of(BinaryFileTestFactory.FILE_ID); + + public static PostfachMail create() { + return createBuilder().build(); + } + + public static PostfachMail createLikeFromClient() { + return createBuilder() + .createdAt(null) + .createdBy(null) + .direction(null) + .build(); + } + + public static PostfachMail.PostfachMailBuilder createBuilder() { + return PostfachMail.builder() + .id(ID) + .vorgangId(VORGANG_ID) + .postfachId(POSTFACH_ID) + .createdAt(CREATED_AT) + .createdBy(CREATED_BY) + .sentAt(SENT_AT) + .sentSuccessful(SENT_SUCCESSFUL) + .messageCode(MESSAGE_CODE) + .direction(DIRECTION) + .subject(SUBJECT) + .mailBody(MAIL_BODY) + .replyOption(REPLY_OPTION) + .attachments(ATTACHMENTS); + } + + public static String buildSendPostfachMailContent() { + return buildSendPostfachMailContent(create()); + } + + public static String buildSendPostfachMailContent(PostfachMail postfachMail) { + return buildSendPostfachMailContent(postfachMail, CommandOrder.SEND_POSTFACH_NACHRICHT); + } + + public static String buildSendPostfachMailContent(PostfachMail postfachMail, CommandOrder order) { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithPostfachMail.json.tmpl", order.name(), + postfachMail.getReplyOption().name(), + TestUtils.addQuote(postfachMail.getSubject()), + TestUtils.addQuote(postfachMail.getMailBody()), + "api/binaryFiles/" + postfachMail.getAttachments().get(0).toString()); + } + + public static String buildResendPostfachMailContent() { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithBody.json.tmpl", CommandOrder.RESEND_POSTFACH_MAIL.name(), null); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtComparatorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtComparatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..47486f527582eeb60c4977377a0a4077c8ff1f4e --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtComparatorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; + +import java.time.ZonedDateTime; + +import org.junit.jupiter.api.Test; + +class PostfachNachrichtComparatorTest { + + private PostfachNachrichtComparator comparator = new PostfachNachrichtComparator(); + + @Test + void shouldCompareCreatedAt() { + var shouldBeFirst = buildPostfachMail(ZonedDateTime.parse("2020-02-01T01:00:00Z"), null); + var shouldBeSecond = buildPostfachMail(ZonedDateTime.parse("2020-01-01T01:00:00Z"), null); + + var result = comparator.compare(shouldBeSecond, shouldBeFirst); + + assertThat(result).isEqualTo(-1); + } + + @Test + void shouldCompareCreatedAtWithSentAtNull() { + var shouldBeFirst = buildPostfachMail(ZonedDateTime.parse("2020-02-01T01:00:00Z"), ZonedDateTime.parse("2020-05-01T01:00:00Z")); + var shouldBeSecond = buildPostfachMail(ZonedDateTime.parse("2020-01-01T01:00:00Z"), null); + + var result = comparator.compare(shouldBeSecond, shouldBeFirst); + + assertThat(result).isEqualTo(-1); + } + + @Test + void shouldCompareSentAt() { + var shouldBeFirst = buildPostfachMail(ZonedDateTime.parse("2020-05-01T01:00:00Z"), ZonedDateTime.parse("2020-10-01T01:00:00Z")); + var shouldBeSecond = buildPostfachMail(ZonedDateTime.parse("2020-10-01T01:00:00Z"), ZonedDateTime.parse("2020-05-01T01:00:00Z")); + + var result = comparator.compare(shouldBeSecond, shouldBeFirst); + + assertThat(result).isEqualTo(-1); + } + + @Test + void shouldCompareSentAtWithCreateAtNull() { + var shouldBeFirst = buildPostfachMail(ZonedDateTime.parse("2020-01-01T01:00:00Z"), ZonedDateTime.parse("2020-10-01T01:00:00Z")); + var shouldBeSecond = buildPostfachMail(null, ZonedDateTime.parse("2020-05-01T01:00:00Z")); + + var result = comparator.compare(shouldBeSecond, shouldBeFirst); + + assertThat(result).isEqualTo(-1); + } + + private PostfachMail buildPostfachMail(ZonedDateTime createdAt, ZonedDateTime sentAt) { + return PostfachMailTestFactory.createBuilder().createdAt(createdAt).sentAt(sentAt).build(); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfDataTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfDataTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..c913fdf0e8044eb9845657f04c785b03ee3ab66a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfDataTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import java.util.List; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class PostfachNachrichtPdfDataTestFactory { + + public static PostfachNachrichtPdfData create() { + return createBuilder().build(); + } + + public static PostfachNachrichtPdfData.PostfachNachrichtPdfDataBuilder createBuilder() { + return PostfachNachrichtPdfData.builder() + .createdAt(PostfachMailTestFactory.CREATED_AT) + .user(UserProfileTestFactory.create()) + .subject(PostfachMailTestFactory.SUBJECT) + .mailBody(PostfachMailTestFactory.MAIL_BODY) + .attachmentNames(List.of(BinaryFileTestFactory.NAME)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..a4ada1e59fa75268a52d42cef5e90924d605d3c3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceITCase.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserRemoteService; +import de.itvsh.goofy.vorgang.EingangTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import lombok.SneakyThrows; + +@SpringBootTest +class PostfachNachrichtPdfServiceITCase { + + @Autowired + private PostfachNachrichtPdfService service; + @MockBean + private UserRemoteService userRemoteService; + + @DisplayName("Generate pdf file") + @Nested + class TestGeneratePdfFile { + + @BeforeEach + void mock() { + when(userRemoteService.getUser(any(UserId.class))).thenReturn(UserProfileTestFactory.create()); + } + + @SneakyThrows + @Test + void generatePdfFile() { + var tempFile = createTempFile(); + + getAllAsPdf(VorgangWithEingangTestFactory.create(), new FileOutputStream(tempFile)); + + assertThat(tempFile).isNotEmpty(); + } + + @SneakyThrows + @Test + void generatePdfFileAntragstellerNotSet() { + var tempFile = createTempFile(); + + getAllAsPdf(buildVorgangAntragstellerNotSet(), new FileOutputStream(tempFile)); + + assertThat(tempFile).isNotEmpty(); + } + + @SneakyThrows + @Test + void generatePdfFileNoAttachments() { + var tempFile = createTempFile(); + + getAllAsPdf(buildVorgangAntragstellerNotSet(), + Stream.of(PostfachNachrichtPdfDataTestFactory.createBuilder().attachmentNames(Collections.emptyList()).build()), + new FileOutputStream(tempFile)); + + assertThat(tempFile).isNotEmpty(); + } + + @SneakyThrows + private File createTempFile() { + var tempFile = File.createTempFile("kop_nachricht_", ".pdf"); + // tempFile.deleteOnExit(); + return tempFile; + } + + @SneakyThrows + private void getAllAsPdf(VorgangWithEingang vorgang, OutputStream out) { + getAllAsPdf(vorgang, buildPostfachMails(), out); + } + + @SneakyThrows + private void getAllAsPdf(VorgangWithEingang vorgang, Stream<PostfachNachrichtPdfData> postfachNachrichten, OutputStream out) { + service.getAllAsPdf(vorgang, postfachNachrichten, out); + out.close(); + } + } + + private VorgangWithEingang buildVorgangAntragstellerNotSet() { + return VorgangWithEingangTestFactory.createBuilder().eingang(EingangTestFactory.createBuilder().antragsteller(null).build()).build(); + } + + private Stream<PostfachNachrichtPdfData> buildPostfachMails() { + return Stream.of(PostfachNachrichtPdfDataTestFactory.createBuilder().subject("hase") + .attachmentNames(List.of("Hase.png", "Hasenlied.mscz", "Erweitertes-Führungszeugniß.pdf", "Haftbefehl.pdf")) + .build(), + PostfachNachrichtPdfDataTestFactory.create(), + PostfachNachrichtPdfDataTestFactory.create(), + PostfachNachrichtPdfDataTestFactory.create(), + PostfachNachrichtPdfDataTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e396dac31372b3480c8d1b20abae859ccbf983a9 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/postfach/PostfachNachrichtPdfServiceTest.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.postfach; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.core.io.Resource; +import org.springframework.util.ReflectionUtils; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.postfach.PostfachMail.Direction; +import de.itvsh.goofy.postfach.PostfachNachrichtPdfModel.Nachricht; +import de.itvsh.goofy.vorgang.AntragstellerTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.kop.common.errorhandling.TechnicalException; +import de.itvsh.kop.common.pdf.PdfService; +import lombok.SneakyThrows; + +class PostfachNachrichtPdfServiceTest { + + @Spy + @InjectMocks + private PostfachNachrichtPdfService service; + @Mock + private PdfService pdfService; + + @DisplayName("Get all as pdf") + @Nested + class TestGetAllAsPdf { + + @Mock + private OutputStream output; + + @DisplayName("on getting template") + @Nested + class TestGetTemplate { + + @Mock + private Resource pdfTemplate; + + @BeforeEach + void mockPdfTemplate() { + var field = ReflectionUtils.findField(PostfachNachrichtPdfService.class, "pdfTemplate"); + field.setAccessible(true); + ReflectionUtils.setField(field, service, pdfTemplate); + } + + @SneakyThrows + @Test + void shouldGetInputStreamFromResource() { + when(pdfTemplate.getInputStream()).thenReturn(InputStream.nullInputStream()); + + service.getTemplate(); + + verify(pdfTemplate).getInputStream(); + } + + @SneakyThrows + @Test + void shouldReturnIfExists() { + var inputStream = InputStream.nullInputStream(); + when(pdfTemplate.getInputStream()).thenReturn(inputStream); + + var templateInputStream = service.getTemplate(); + + assertThat(templateInputStream).isEqualTo(inputStream); + } + + @SneakyThrows + @Test + void shouldThrowExceptionIfMissing() { + when(pdfTemplate.getInputStream()).thenThrow(IOException.class); + + assertThatThrownBy(() -> service.getTemplate()).isInstanceOf(TechnicalException.class); + } + } + + @DisplayName("build model") + @Nested + class TestBuildModel { + + @DisplayName("by vorgang") + @Nested + class TestByVorgang { + + @Test + void shouldSetVorgangNummer() { + var model = service.buildModel(VorgangWithEingangTestFactory.create(), Stream.empty()); + + assertThat(model.getVorgangNummer()).isEqualTo(VorgangHeaderTestFactory.NUMMER); + } + + @Test + void shouldSetVorgangName() { + var model = service.buildModel(VorgangWithEingangTestFactory.create(), Stream.empty()); + + assertThat(model.getVorgangName()).isEqualTo(VorgangHeaderTestFactory.NAME); + } + + @Nested + class TestSetIsFirst { + + @Test + void shouldSetIsFirstTrueOnFirstMessage() { + var model = service.buildModel(VorgangWithEingangTestFactory.create(), + Stream.of(PostfachNachrichtPdfDataTestFactory.create())); + + assertThat(model.getNachrichten().get(0).isFirst()).isTrue(); + } + + @Test + void shouldSetIsFirstFalseOnOtherMessage() { + var model = service.buildModel(VorgangWithEingangTestFactory.create(), + Stream.of(PostfachNachrichtPdfDataTestFactory.create(), PostfachNachrichtPdfDataTestFactory.create(), + PostfachNachrichtPdfDataTestFactory.create())); + + assertThat(model.getNachrichten().get(1).isFirst()).isFalse(); + assertThat(model.getNachrichten().get(2).isFirst()).isFalse(); + } + } + } + + @DisplayName("by Antragsteller") + @Nested + class TestMapAntragsteller { + + @Test + void shouldMapAntragstellerAnrede() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerAnrede()).isEqualTo(AntragstellerTestFactory.ANREDE); + } + + @Test + void shouldMapAntragstellerVorname() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerVorname()).isEqualTo(AntragstellerTestFactory.VORNAME); + } + + @Test + void shouldMapAntragstellerNachname() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerNachname()).isEqualTo(AntragstellerTestFactory.NACHNAME); + } + + @Test + void shouldMapAntragstellerStrasse() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerStrasse()).isEqualTo(AntragstellerTestFactory.STRASSE); + } + + @Test + void shouldMapAntragstellerHausnummer() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerHausnummer()).isEqualTo(AntragstellerTestFactory.HAUSNUMMER); + } + + @Test + void shouldMapAntragstellerPlz() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerPlz()).isEqualTo(AntragstellerTestFactory.PLZ); + } + + @Test + void shouldMapAntragstellerOrt() { + var modelBuilder = mapAntragsteller(); + + assertThat(modelBuilder.build().getAntragstellerOrt()).isEqualTo(AntragstellerTestFactory.ORT); + } + + private PostfachNachrichtPdfModel.PostfachNachrichtPdfModelBuilder mapAntragsteller() { + var modelBuilder = PostfachNachrichtPdfModel.builder(); + + service.mapAntragsteller(modelBuilder, AntragstellerTestFactory.create()); + + return modelBuilder; + } + } + + @DisplayName("by postfachnachricht pdf data") + @Nested + class TestMapPostfachNachricht { + + @Test + void shouldMapNachrichtSubject() { + var nachricht = mapNachricht(); + + assertThat(nachricht.getSubject()).isEqualTo(PostfachMailTestFactory.SUBJECT); + } + + @Test + void shouldMapNachrichtMailBody() { + var nachricht = mapNachricht(); + + assertThat(nachricht.getMailBody()).isEqualTo(PostfachMailTestFactory.MAIL_BODY); + } + + @Test + void shouldMapNachrichtCreatedAt() { + var nachricht = mapNachricht(); + + assertThat(nachricht.getCreatedAt()).isEqualTo("01.01.2000 01:00:00"); + } + + @Test + void shouldMapNachrichtCreatedBy() { + var nachricht = mapNachricht(); + + assertThat(nachricht.getCreatedBy()).isEqualTo(UserProfileTestFactory.FULLNAME); + } + + @Test + void shouldMapNachrichtAttachments() { + var nachricht = mapNachricht(); + + assertThat(nachricht.getAttachments()).containsExactly(BinaryFileTestFactory.NAME); + } + + private Nachricht mapNachricht() { + return service.mapPostfachNachricht(PostfachNachrichtPdfDataTestFactory.create(), AntragstellerTestFactory.create()); + } + + @DisplayName("for incoming nachricht") + @Nested + class TestDirectionIn { + + @DisplayName("build absender name") + @Nested + class TestBuildUserName { + + private final PostfachNachrichtPdfData data = PostfachNachrichtPdfDataTestFactory.createBuilder().direction(Direction.IN) + .build(); + + @Test + void shouldReturnAntragstellerIfExists() { + var name = service.buildAbsenderName(data, AntragstellerTestFactory.create()); + + assertThat(name).isEqualTo(AntragstellerTestFactory.VORNAME + " " + AntragstellerTestFactory.NACHNAME); + } + + @Test + void shouldReturnFallbackNameForAntragsteller() { + var name = service.buildAbsenderName(data, null); + + assertThat(name).isEqualTo(PostfachNachrichtPdfService.FALLBACK_ANTRAGSTELLER_NAME); + } + + @Test + void shouldReturnEmptyStringOnNullAntragstellerName() { + var name = service.buildAbsenderName(data, AntragstellerTestFactory.createBuilder().vorname(null).nachname(null).build()); + + assertThat(name).isEqualTo(StringUtils.EMPTY); + } + } + } + + @DisplayName("for outgoing nachricht") + @Nested + class TestDirectionOut { + + @DisplayName("build absender name") + @Nested + class TestBuildUserName { + + private final PostfachNachrichtPdfData data = PostfachNachrichtPdfDataTestFactory.createBuilder().direction(Direction.OUT) + .build(); + + @Test + void shouldReturnUserProfileNameIfExists() { + var name = service.buildAbsenderName(data, AntragstellerTestFactory.create()); + + assertThat(name).isEqualTo(UserProfileTestFactory.FIRSTNAME + " " + UserProfileTestFactory.LASTNAME); + } + + @Test + void shouldReturnFallbackNameForUserProfile() { + var name = service.buildAbsenderName( + PostfachNachrichtPdfDataTestFactory.createBuilder().direction(Direction.OUT).user(null).build(), + AntragstellerTestFactory.create()); + + assertThat(name).isEqualTo(PostfachNachrichtPdfService.FALLBACK_USER_NAME); + } + + @Test + void shouldReturnEmptyStringOnNullName() { + var data = PostfachNachrichtPdfDataTestFactory.createBuilder().direction(Direction.OUT) + .user(UserProfileTestFactory.createBuilder().firstName(null).lastName(null).build()).build(); + + var name = service.buildAbsenderName(data, AntragstellerTestFactory.create()); + + assertThat(name).isEmpty(); + } + } + } + } + } + + @Test + void shouldCallPdfService() { + doReturn(InputStream.nullInputStream()).when(service).getTemplate(); + + service.getAllAsPdf(VorgangWithEingangTestFactory.create(), Stream.of(PostfachNachrichtPdfDataTestFactory.create()), output); + + verify(pdfService).createPdf(any(InputStream.class), eq(output), any(PostfachNachrichtPdfModel.class)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/representation/GrpcGetRepresentationsResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/representation/GrpcGetRepresentationsResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..39d329d667b1ce4aa45c94d8fc3338d565da5f67 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/representation/GrpcGetRepresentationsResponseTestFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.representation; + +import java.util.Collections; + +import de.itvsh.goofy.common.file.GrpcOzgFileTestFactory; +import de.itvsh.ozg.pluto.grpc.file.GrpcGetRepresentationsResponse; +import de.itvsh.ozg.pluto.grpc.file.GrpcOzgFile; + +public class GrpcGetRepresentationsResponseTestFactory { + + private static GrpcOzgFile GRPC_FILE = GrpcOzgFileTestFactory.create(); + + public static GrpcGetRepresentationsResponse create() { + return createBuilder().build(); + } + + public static GrpcGetRepresentationsResponse.Builder createBuilder() { + return GrpcGetRepresentationsResponse.newBuilder() + .addAllFile(Collections.singleton(GRPC_FILE)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/representation/RepresentationControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/representation/RepresentationControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7b95e7fabe95fbe3e931ab4e435abb721f0318f3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/representation/RepresentationControllerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.representation; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileModelAssembler; +import de.itvsh.goofy.common.file.OzgFile; +import de.itvsh.goofy.common.file.OzgFileService; +import de.itvsh.goofy.common.file.OzgFileTestFactory; + +class RepresentationControllerTest { + + private final String PATH = RepresentationController.REPRESENTATIONS_PATH; + + @InjectMocks + private RepresentationController controller; + @Mock + private BinaryFileModelAssembler modelAssembler; + @Mock + private OzgFileService fileService; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetRepresentationsByEingang { + + @BeforeEach + void mockFileService() { + when(fileService.getRepresentationsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); + } + + @Test + void shouldCallEndpoint() throws Exception { + callEndpoint(); + } + + @Test + void shoudlCallFileService() throws Exception { + callEndpoint(); + + verify(fileService).getRepresentationsByEingang(any()); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); + } + + private void callEndpoint() throws Exception { + mockMvc.perform(get(PATH).param(RepresentationController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/system/GrpcGetSystemStatusResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/system/GrpcGetSystemStatusResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..540f0951ee1486e5104fbd8c37817d0edfc740bf --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/system/GrpcGetSystemStatusResponseTestFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import de.itvsh.ozg.pluto.system.GrpcGetSystemStatusResponse; + +public class GrpcGetSystemStatusResponseTestFactory { + + public static GrpcGetSystemStatusResponse create() { + return createBuilder().build(); + } + + public static GrpcGetSystemStatusResponse.Builder createBuilder() { + return GrpcGetSystemStatusResponse.newBuilder() + .setIsSearchServerAvailable(true) + .setJavaVersion(PlutoSystemStatusTestFactory.JAVA_VERSION) + .setBuildVersion(PlutoSystemStatusTestFactory.BUILD_VERSION); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/system/PlutoSystemStatusTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/system/PlutoSystemStatusTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..55b5a3e5ba5c8760dd73d964d369d1c9b3541e2d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/system/PlutoSystemStatusTestFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import de.itvsh.goofy.system.PlutoSystemStatus.PlutoSystemStatusBuilder; + +public class PlutoSystemStatusTestFactory { + + public static final String JAVA_VERSION = "17"; + public static final String BUILD_VERSION = "b1"; + + public static PlutoSystemStatus create() { + return createBuilder().build(); + } + + private static PlutoSystemStatusBuilder createBuilder() { + return PlutoSystemStatus.builder() + .isSearchServerAvailable(true) + .javaVersion(JAVA_VERSION) + .buildVersion(BUILD_VERSION); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/system/RemoteSystemStatusMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/system/RemoteSystemStatusMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..835faacd705c078fce895b72a8bdbb19687db4aa --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/system/RemoteSystemStatusMapperTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.*; + +class RemoteSystemStatusMapperTest { + + private RemoteSystemStatusMapper mapper = Mappers.getMapper(RemoteSystemStatusMapper.class); + + @Test + @DisplayName("should map gRPC response to PlutoSystemStatus") + void shouldMapGrpcResponse() { + var plutoSystemStatus = mapper.fromGrpc(GrpcGetSystemStatusResponseTestFactory.create()); + + assertThat(plutoSystemStatus).isEqualTo(PlutoSystemStatusTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/system/SystemStatusRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/system/SystemStatusRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1acc5bd8cfd75ccdff3bb4b4d57582358dba5ad6 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/system/SystemStatusRemoteServiceTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.system; + +import de.itvsh.ozg.pluto.system.GrpcGetSystemStatusRequest; +import de.itvsh.ozg.pluto.system.SystemStatusServiceGrpc.SystemStatusServiceBlockingStub; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +class SystemStatusRemoteServiceTest { + + @InjectMocks + private SystemStatusRemoteService service; + + @Mock + private SystemStatusServiceBlockingStub serviceStub; + @Mock + private RemoteSystemStatusMapper mapper; + + @Nested + class TestGetSystemStatus { + + private static final GrpcGetSystemStatusRequest SYSTEM_STATUS_REQUEST = GrpcGetSystemStatusRequest.newBuilder().build(); + private static final PlutoSystemStatus PLUTO_SYSTEM_STATUS = PlutoSystemStatusTestFactory.create(); + + @BeforeEach + void init() { + when(mapper.fromGrpc(any())).thenReturn(PLUTO_SYSTEM_STATUS); + } + + @Test + void shouldCallRemoteService() { + service.getSystemStatus(); + + verify(serviceStub).getSystemStatus(SYSTEM_STATUS_REQUEST); + } + + @Test + void shouldGetSearchServerStatus() { + when(serviceStub.getSystemStatus(any())).thenReturn(GrpcGetSystemStatusResponseTestFactory.create()); + + var plutoSystemStatus = service.getSystemStatus(); + + assertThat(plutoSystemStatus).isEqualTo(PLUTO_SYSTEM_STATUS); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..27aafd8faa13a3b641421f7c53176cee79de8440 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerMapperTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +import de.itvsh.kop.pluto.common.grpc.GrpcFormDataMapper; + +class AntragstellerMapperTest { + + @InjectMocks + private final AntragstellerMapper mapper = Mappers.getMapper(AntragstellerMapper.class); + @Spy + private final GrpcFormDataMapper grpcFormDataMapper = Mappers.getMapper(GrpcFormDataMapper.class); + + @Test + void shouldMap() { + var antragsteller = mapper.toAntragsteller(GrpcAntragstellerTestFactory.create()); + + assertThat(antragsteller).usingRecursiveComparison().ignoringFields("otherData").isEqualTo(AntragstellerTestFactory.create()); + } + + @Test + void shouldCallGrpcFormDataMapper() { + mapper.toAntragsteller(GrpcAntragstellerTestFactory.create()); + + verify(grpcFormDataMapper).mapFromFormData(GrpcAntragstellerTestFactory.OTHER_DATA); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..cc86bacb70a535fa6f5ef7fe3c87c44593a97998 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/AntragstellerTestFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Map; + +public class AntragstellerTestFactory { + + public final static String ANREDE = GrpcAntragstellerTestFactory.ANREDE; + public final static String NACHNAME = GrpcAntragstellerTestFactory.NACHNAME; + public final static String VORNAME = GrpcAntragstellerTestFactory.VORNAME; + public final static String GEBURTSDATUM_STR = GrpcAntragstellerTestFactory.GEBURTSDATUM_STR; + public final static String GEBURTSORT = GrpcAntragstellerTestFactory.GEBURTSORT; + public final static String GEBURTSNAME = GrpcAntragstellerTestFactory.GEBURTSNAME; + public final static String EMAIL = GrpcAntragstellerTestFactory.EMAIL; + public final static String TELEFON = GrpcAntragstellerTestFactory.TELEFON; + public final static String STRASSE = GrpcAntragstellerTestFactory.STRASSE; + public final static String HAUSNUMMER = GrpcAntragstellerTestFactory.HAUSNUMMER; + public final static String PLZ = GrpcAntragstellerTestFactory.PLZ; + public final static String ORT = GrpcAntragstellerTestFactory.ORT; + public final static String POSTFACH_ID = GrpcAntragstellerTestFactory.POSTFACH_ID; + + public final static String OTHER_DATA_KEY = "Test"; + public final static String OTHER_DATA_VALUE = "Eintrag"; + + public static final Map<String, Object> OTHER_DATA = Map.of(OTHER_DATA_KEY, OTHER_DATA_VALUE); + + public static Antragsteller create() { + return createBuilder().build(); + } + + public static Antragsteller.AntragstellerBuilder createBuilder() { + return Antragsteller.builder() + .anrede(ANREDE) + .vorname(VORNAME) + .nachname(NACHNAME) + .geburtsdatum(GEBURTSDATUM_STR) + .geburtsname(GEBURTSNAME) + .geburtsort(GEBURTSORT) + .email(EMAIL) + .telefon(TELEFON) + .strasse(STRASSE) + .hausnummer(HAUSNUMMER) + .plz(PLZ) + .ort(ORT) + .postfachId(POSTFACH_ID) + .otherData(OTHER_DATA); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ClientAttributeUtilsTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ClientAttributeUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..89ced0248fb47f2195373a99c4aa15e7dda3bfce --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ClientAttributeUtilsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import de.itvsh.goofy.common.clientattribute.GrpcClientAttributeTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; + +class ClientAttributeUtilsTest { + @Nested + class TestClientAttributeUtils { + List<GrpcClientAttribute> attributeList; + + @BeforeEach + void init() { + attributeList = List.of( + GrpcClientAttributeTestFactory.createBuilder().setAttributeName("name1").build(), + GrpcClientAttributeTestFactory.create(), + GrpcClientAttributeTestFactory.createBuilder().setAttributeName("name2").build()); + } + + @Test + void findByName() { + assertThat(ClientAttributeUtils.findByName(GrpcClientAttributeTestFactory.ATTRIBUTE_NAME, attributeList)).isNotNull(); + } + + @Test + void findNotByName() { + assertThat(ClientAttributeUtils.findByName("not_in_list", attributeList)).isEmpty(); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e5f834cf117c9ecc130b1a0b88ecc94363b0abe3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderMapperTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class EingangHeaderMapperTest { + + private EingangHeaderMapper mapper = Mappers.getMapper(EingangHeaderMapper.class); + + @Nested + class TestGrpcEingangHeaderToGoofyEingangHeader { + + @Test + void shouldMapCreatedAt() { + var eingang = callMapper(); + + assertThat(eingang.getCreatedAt()).isEqualTo(GrpcEingangHeaderTestFactory.CREATED_AT); + } + + @Test + void shouldMapRequestId() { + var eingang = callMapper(); + + assertThat(eingang.getRequestId()).isEqualTo(GrpcEingangHeaderTestFactory.REQUEST_ID); + } + + private EingangHeader callMapper() { + return mapper.toEingangHeader(GrpcEingangHeaderTestFactory.create()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..b4f89ae2ad24314281e11ea1bfadd0b489888c6a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangHeaderTestFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +public class EingangHeaderTestFactory { + + public static EingangHeader create() { + return createBuilder().build(); + } + + public static EingangHeader.EingangHeaderBuilder createBuilder() { + return EingangHeader.builder(); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0f6f13dae72731e1ce2b682e486d50b0e58a9bb0 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangMapperTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.kop.pluto.common.grpc.GrpcFormDataMapper; +import de.itvsh.ozg.pluto.vorgang.GrpcEingang; + +class EingangMapperTest { + + @InjectMocks + private EingangMapper mapper = Mappers.getMapper(EingangMapper.class); + + @Mock + private AntragstellerMapper antragstellerMapper; + @Mock + private EingangHeaderMapper eingangHeaderMapper; + @Spy + private GrpcFormDataMapper formDataMapper = Mappers.getMapper(GrpcFormDataMapper.class); + + @Nested + class TestGrpcEingangToEingang { + + private final Antragsteller antragsteller = AntragstellerTestFactory.create(); + private final EingangHeader eingangHeader = EingangHeaderTestFactory.create(); + + @BeforeEach + void mockMapperReturnValues() { + lenient().when(antragstellerMapper.toAntragsteller(any())).thenReturn(antragsteller); + lenient().when(eingangHeaderMapper.toEingangHeader(any())).thenReturn(eingangHeader); + } + + @Test + void shouldMapIdToId() { + GrpcEingang grpcEingang = GrpcEingangTestFactory.create(); + + var eingang = mapper.fromGrpc(grpcEingang); + + assertThat(eingang.getId()).isEqualTo(GrpcEingangTestFactory.ID); + } + + @Test + void shouldHaveAntragsteller() { + GrpcEingang grpcEingang = GrpcEingangTestFactory.create(); + + var eingang = mapper.fromGrpc(grpcEingang); + + assertThat(eingang.getAntragsteller()).isEqualTo(antragsteller); + } + + @Test + void shouldHaveEingangHeader() { + GrpcEingang grpcEingang = GrpcEingangTestFactory.create(); + + var eingang = mapper.fromGrpc(grpcEingang); + + assertThat(eingang.getHeader()).isEqualTo(eingangHeader); + } + + @Test + void shouldProceedWithEmptyValues() { + GrpcEingang grpcEingang = GrpcEingang.newBuilder().build(); + + var eingang = mapper.fromGrpc(grpcEingang); + + assertThat(eingang.getAntragsteller()).isNull(); + assertThat(eingang.getHeader()).isNull(); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d306803e7cb0e6d931e01d0b28339c1e6edbbb0d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/EingangTestFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Map; +import java.util.UUID; + +public class EingangTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + + public static final String SINGLE_FIELD_NAME = "name"; + public static final String SINGLE_FIELD_VALUE = "Thea"; + public static final String NUMERIC_FIELD_NAME = "alter"; + public static final Integer NUMERIC_FIELD_VALUE = 42; + + public static final String SUBFORM_FIELD_NAME = "E-Mail"; + public static final String SUBFORM_FIELD_VALUE = "thea@burger-portal.sh.de"; + public static final String SUBFORM_NAME = "kontakt"; + public static final Map<String, Object> SUBFORM = Map.of(SUBFORM_FIELD_NAME, SUBFORM_FIELD_VALUE); + + public static final Map<String, Object> AS_MAP = Map.of(SINGLE_FIELD_NAME, SINGLE_FIELD_VALUE, SUBFORM_NAME, SUBFORM); + + public static Eingang create() { + return createBuilder().build(); + } + + public static Eingang.EingangBuilder createBuilder() { + return Eingang.builder() + .id(ID) + .antragsteller(AntragstellerTestFactory.create()) + .header(EingangHeaderTestFactory.create()) + .zustaendigeStelle(ZustaendigeStelleTestFactory.create()) + .formData(Map.of(SINGLE_FIELD_NAME, SINGLE_FIELD_VALUE, + NUMERIC_FIELD_NAME, NUMERIC_FIELD_VALUE, + SUBFORM_NAME, SUBFORM)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/FindVorgaengeRequestCriteriaTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/FindVorgaengeRequestCriteriaTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2cffb2acbe6c3339ebcfb011e4efa1ea2720ebed --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/FindVorgaengeRequestCriteriaTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.Optional; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class FindVorgaengeRequestCriteriaTestFactory { + + public static int LIMIT = 100; + public static int OFFSET = 0; + public static String SEARCH_BY = LoremIpsum.getInstance().getWords(2); + public static OrderBy ORDER_BY = OrderBy.PRIORITY; + + public static FindVorgaengeHeaderRequestCriteria create() { + return createBuilder().build(); + } + + public static FindVorgaengeHeaderRequestCriteria.FindVorgaengeHeaderRequestCriteriaBuilder createBuilder() { + return FindVorgaengeHeaderRequestCriteria.builder() + .limit(LIMIT) + .offset(OFFSET) + .searchBy(Optional.of(SEARCH_BY)) + .orderBy(ORDER_BY) + .assignedTo(Optional.of(UserProfileTestFactory.ID)); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcAntragstellerTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcAntragstellerTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..bf72d78420b89af619477bd5af76176839627a23 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcAntragstellerTestFactory.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.ozg.pluto.vorgang.GrpcAntragsteller; +import de.itvsh.ozg.pluto.vorgang.GrpcFormData; +import de.itvsh.ozg.pluto.vorgang.GrpcFormField; + +public class GrpcAntragstellerTestFactory { + + public final static String ANREDE = "HERR/FRAU"; + public final static String NACHNAME = LoremIpsum.getInstance().getLastName(); + public final static String VORNAME = LoremIpsum.getInstance().getFirstName(); + public final static String GEBURTSDATUM_STR = "1995-03-21"; + public final static String GEBURTSORT = LoremIpsum.getInstance().getCountry(); + public final static String GEBURTSNAME = LoremIpsum.getInstance().getFirstName(); + public final static String EMAIL = LoremIpsum.getInstance().getEmail(); + public static final String TELEFON = "+ 49 4621 9654"; + public static final String STRASSE = "Lachstrasse"; + public static final String HAUSNUMMER = "8484"; + public static final String PLZ = "12345"; + public static final String ORT = "Wohlfuehlhausen"; + public static final String POSTFACH_ID = UUID.randomUUID().toString(); + + public final static GrpcFormData OTHER_DATA = GrpcFormData.newBuilder() + .addField(GrpcFormField.newBuilder() + .setName(AntragstellerTestFactory.OTHER_DATA_KEY) + .setValue(AntragstellerTestFactory.OTHER_DATA_VALUE).build()) + .build(); + + public static GrpcAntragsteller create() { + return createBuilder().build(); + } + + public static GrpcAntragsteller.Builder createBuilder() { + return GrpcAntragsteller.newBuilder() + .setAnrede(ANREDE) + .setGeburtsdatum(GEBURTSDATUM_STR) + .setNachname(NACHNAME) + .setVorname(VORNAME) + .setGeburtsname(GEBURTSNAME) + .setGeburtsort(GEBURTSORT) + .setEmail(EMAIL) + .setTelefon(TELEFON) + .setStrasse(STRASSE) + .setHausnummer(HAUSNUMMER) + .setPlz(PLZ) + .setOrt(ORT) + .setPostfachId(POSTFACH_ID) + .setOtherData(OTHER_DATA); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcClientAttributeTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcClientAttributeTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e174266e6ceb9c7fc104496ccb9f99dde64a02eb --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcClientAttributeTestFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.itvsh.goofy.vorgang; + +import de.itvsh.goofy.common.callcontext.CallContextTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcAccessPermission; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute.Builder; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; + +class GrpcClientAttributeTestFactory { + + static final String CLIENT = CallContextTestFactory.CLIENT_NAME; + static final GrpcAccessPermission ACCESS = GrpcAccessPermission.READ_WRITE; + + static final GrpcClientAttribute create() { + return createBuilder().build(); + } + + static final Builder createBuilder() { + return GrpcClientAttribute.newBuilder() + .setClientName(CLIENT) + .setAccess(ACCESS); + } + + static final GrpcClientAttribute createWith(String attributeName, boolean value) { + return createBuilder().setAttributeName(attributeName) + .setValue(GrpcClientAttributeValue.newBuilder().setBoolValue(value).build()) + .build(); + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangHeaderTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangHeaderTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..6496d0fd6ea1a8ff9acdca68621a79213d74ac01 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangHeaderTestFactory.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.ozg.pluto.vorgang.GrpcEingangHeader; + +public class GrpcEingangHeaderTestFactory { + + public final static String REQUEST_ID = UUID.randomUUID().toString(); + private final static String CREATED_AT_STR = "2021-01-10T10:30:00Z"; + public final static ZonedDateTime CREATED_AT = ZonedDateTime.parse(CREATED_AT_STR); + private final static String FORM_ID = UUID.randomUUID().toString(); + public final static String FORM_NAME = LoremIpsum.getInstance().getWords(1); + private final static String SENDER = LoremIpsum.getInstance().getName(); + private final static String CUSTOMER = LoremIpsum.getInstance().getName(); + private final static String CUSTOMER_ID = UUID.randomUUID().toString(); + private final static String CLIENT = LoremIpsum.getInstance().getName(); + private final static String CLIENT_ID = UUID.randomUUID().toString(); + + public static GrpcEingangHeader create() { + return createBuilder().build(); + } + + public static GrpcEingangHeader.Builder createBuilder() { + return GrpcEingangHeader.newBuilder() + .setRequestId(REQUEST_ID) + .setCreatedAt(CREATED_AT_STR) + .setFormId(FORM_ID) + .setFormName(FORM_NAME) + .setSender(SENDER) + .setCustomer(CUSTOMER) + .setCustomerId(CUSTOMER_ID) + .setClient(CLIENT) + .setClientId(CLIENT_ID); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..22b544cb0afe67d0fce833cef23d440545647149 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcEingangTestFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.UUID; + +import de.itvsh.ozg.pluto.vorgang.GrpcEingang; +import de.itvsh.ozg.pluto.vorgang.GrpcFormData; +import de.itvsh.ozg.pluto.vorgang.GrpcSubForm; + +public class GrpcEingangTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final GrpcSubForm SUB_FORM = GrpcSubFormTestFactory.create(); + + public static GrpcEingang create() { + return createBuilder().build(); + } + + public static GrpcEingang.Builder createBuilder() { + return GrpcEingang.newBuilder() + .setId(ID) + .setHeader(GrpcEingangHeaderTestFactory.create()) + .setAntragsteller(GrpcAntragstellerTestFactory.create()) + .setFormData(GrpcFormData.newBuilder().addForm(GrpcSubFormTestFactory.create()).build()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFilterByTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFilterByTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2e8ccb0747c8b5f76c86001c0c58f5e15fc9a8c8 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFilterByTestFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.ozg.pluto.vorgang.GrpcFilterBy; + +public class GrpcFilterByTestFactory { + + static final boolean FILTER_BY_ORGANISATIONSEINHEITEN_ID = true; + + public static GrpcFilterBy create() { + return createBuilder().build(); + } + + public static GrpcFilterBy.Builder createBuilder() { + return GrpcFilterBy.newBuilder() + .setFilterByOrganisationseinheitenId(FILTER_BY_ORGANISATIONSEINHEITEN_ID) + .addStatus(VorgangStatus.NEU.name()) + .addOrganisationseinheitId(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFindVorgangRequestTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFindVorgangRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..eb628a4da330d9bd9b5175963846b1a13f392f9f --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFindVorgangRequestTestFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.FindVorgaengeRequestCriteriaTestFactory.*; + +import de.itvsh.ozg.pluto.vorgang.GrpcFilterBy; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangRequest; + +public class GrpcFindVorgangRequestTestFactory { + + final static GrpcFilterBy FILTER_BY = GrpcFilterByTestFactory.create(); + + public static GrpcFindVorgangRequest create() { + return createBuilder().build(); + } + + public static GrpcFindVorgangRequest.Builder createBuilder() { + return GrpcFindVorgangRequest.newBuilder() + .setFilterBy(FILTER_BY) + .setSearchBy(SEARCH_BY) + .setLimit(LIMIT) + .setOffset(OFFSET); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormDataMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormDataMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4aa87a74e8029bcde863721a02609a0e88eeed8d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormDataMapperTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.kop.pluto.common.grpc.GrpcFormDataMapper; +import de.itvsh.ozg.pluto.vorgang.GrpcFormData; +import de.itvsh.ozg.pluto.vorgang.GrpcFormField; +import de.itvsh.ozg.pluto.vorgang.GrpcSubForm; + +class GrpcFormDataMapperTest { + + @InjectMocks + private GrpcFormDataMapper MAPPER_INSTANCE = Mappers.getMapper(GrpcFormDataMapper.class); + + @Mock + private GrpcFormDataMapper grpcFormDataMapper; + + @BeforeEach + void mockMapperReturnValues() { + lenient().when(grpcFormDataMapper.mapToFormData(any())) + .thenReturn(GrpcFormData.newBuilder().addField(GrpcFormFieldTestFactory.create()).build()); + } + + @Nested + class TestSimpleValueMapping { + + @Test + void shouldHaveField() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", "value")); + + assertThat(formData.getFieldList()).hasSize(1); + } + + @Test + void shouldNotHaveSubForms() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", "value")); + + assertThat(formData.getFormList()).isEmpty(); + } + + @Test + void shouldHaveNameField() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", "value")); + + assertThat(formData.getField(0).getName()).isEqualTo("key"); + assertThat(formData.getField(0).getValue()).isEqualTo("value"); + } + } + + @Nested + class TestSubFormMapping { + + @Test + void shouldHaveSubForm() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", Map.of("subKey", "value"))); + + assertThat(formData.getFormList()).hasSize(1); + } + + @Test + void shouldNotHaveFields() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", Map.of("subKey", "value"))); + + assertThat(formData.getFieldList()).isEmpty(); + } + + @Test + void shouldHaveSubFormField() { + + GrpcFormData formData = MAPPER_INSTANCE.mapToFormData(Map.of("key", Map.of("subKey", "value"))); + + assertThat(formData.getForm(0).getFieldList()).hasSize(1); + assertThat(formData.getForm(0).getTitle()).isEqualTo("key"); + assertThat(formData.getForm(0).getField(0).getName()).isEqualTo("subKey"); + assertThat(formData.getForm(0).getField(0).getValue()).isEqualTo("value"); + } + } + + @Nested + class TestMapStringListsToFields { + + @Test + void emptyMapShouldNotBeMapped() { + + List<GrpcFormField> fields = MAPPER_INSTANCE.mapStringListsToFields(Collections.emptyMap()); + + assertThat(fields).isEmpty(); + } + + @Test + void simpleValueShouldNotBeMapped() { + + List<GrpcFormField> fields = MAPPER_INSTANCE.mapStringListsToFields(Map.of("a", "b")); + + assertThat(fields).isEmpty(); + } + + @Test + void listObjectValuesShouldBeMapped() { + + List<GrpcFormField> fields = MAPPER_INSTANCE.mapStringListsToFields(Map.of("key", List.of("value1", "value2"))); + + assertThat(fields).hasSize(2); + assertThat(fields.get(0).getName()).isEqualTo("key"); + assertThat(fields.get(0).getValue()).isEqualTo("value1"); + + assertThat(fields.get(1).getName()).isEqualTo("key"); + assertThat(fields.get(1).getValue()).isEqualTo("value2"); + } + } + + @Nested + class TestMapObjectListsToFields { + + @Test + void simpleValueShouldNotBeMapped() { + + List<GrpcSubForm> fields = MAPPER_INSTANCE.mapObjectListsToFields(Map.of("a", "b")); + + assertThat(fields).isEmpty(); + } + + @Test + void listOfSimpleValueShouldNotBeMapped() { + + List<GrpcSubForm> fields = MAPPER_INSTANCE.mapObjectListsToFields(Map.of("a", List.of("l1", "l2"))); + + assertThat(fields).isEmpty(); + } + + @Test + void listOfObjectsShouldBeMappedToSubForms() { + + List<GrpcSubForm> fields = MAPPER_INSTANCE + .mapObjectListsToFields(Map.of("key", List.of(Collections.emptyMap(), Collections.emptyMap()))); + + assertThat(fields).hasSize(2); + } + + @Test + void listOfObjectsShouldMappedToSubFormTitle() { + + List<GrpcSubForm> fields = MAPPER_INSTANCE + .mapObjectListsToFields(Map.of("key", List.of(Collections.emptyMap(), Collections.emptyMap()))); + + assertThat(fields.get(0).getTitle()).isEqualTo("key"); + assertThat(fields.get(1).getTitle()).isEqualTo("key"); + } + + @Test + void listOfObjectsShouldBeMapped() { + + List<GrpcSubForm> fields = MAPPER_INSTANCE + .mapObjectListsToFields(Map.of("key", List.of(Map.of("a1", "a2")))); + + assertThat(fields.get(0).getFieldCount()).isEqualTo(1); + assertThat(fields.get(0).getField(0).getName()).isEqualTo("a1"); + assertThat(fields.get(0).getField(0).getValue()).isEqualTo("a2"); + } + + @Test + void doubleNestedListObjectValuesShouldBeMapped() { + GrpcFormData formData = MAPPER_INSTANCE + .mapToFormData(Map.of("key1", Map.of("key2", List.of("value1", "value2")))); + + assertThat(formData.getForm(0).getFieldCount()).isEqualTo(2); + } + + @Test + void multipleNestedListObjectValuesShouldBeMapped() { + GrpcFormData formData = MAPPER_INSTANCE + .mapToFormData(Map.of("key1", Map.of("key2", Map.of("key3", List.of("value1", "value2"))))); + + assertThat(formData.getForm(0).getSubForm(0).getFieldCount()).isEqualTo(2); + } + } + + @Nested + class TestMapListOfMixedValuesInFormData { + + @Test + void shouldMapListOfStrings() { + + GrpcFormData formData = MAPPER_INSTANCE + .mapToFormData(Map.of("key", List.of("value1", "value2", Map.of("internKey1", "internValue1")))); + + assertThat(formData.getFieldCount()).isEqualTo(2); + assertThat(formData.getFormCount()).isEqualTo(1); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormFieldTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormFieldTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f110b59201a0f49d7101c48b76d97db274e61128 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcFormFieldTestFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.ozg.pluto.vorgang.GrpcFormField; + +public class GrpcFormFieldTestFactory { + + public static final String TEST_NAME = "name"; + public static final String TEST_VALUE = "value"; + + public static GrpcFormField create() { + return createBuilder().build(); + } + + private static GrpcFormField.Builder createBuilder() { + return GrpcFormField.newBuilder() + .setName(TEST_NAME) + .setValue(TEST_VALUE); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcSubFormTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcSubFormTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e6970f8f9c7ccbc176512811b606f3ab48295f1d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcSubFormTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.ozg.pluto.vorgang.GrpcFormField; +import de.itvsh.ozg.pluto.vorgang.GrpcSubForm; + +public class GrpcSubFormTestFactory { + + public static final String TITLE = "person"; + + public static final String FIELD_NAME = EingangTestFactory.SINGLE_FIELD_NAME; + public static final String FIELD_VALUE = EingangTestFactory.SINGLE_FIELD_VALUE; + + public static final String SUBFORM_NAME = EingangTestFactory.SUBFORM_NAME; + public static final String SUBFORM_FIELD_NAME = EingangTestFactory.SUBFORM_FIELD_NAME; + public static final String SUBFORM_FIELD_VALUE = EingangTestFactory.SUBFORM_FIELD_VALUE; + + public static GrpcSubForm create() { + return createBuilder().build(); + } + + public static GrpcSubForm.Builder createBuilder() { + return GrpcSubForm.newBuilder() + .setTitle(TITLE) + .addField(GrpcFormField.newBuilder().setName(FIELD_NAME).setValue(FIELD_VALUE).build()) + .addSubForm(GrpcSubForm.newBuilder().setTitle(SUBFORM_NAME).addField( + GrpcFormField.newBuilder().setName(SUBFORM_FIELD_NAME).setValue(SUBFORM_FIELD_VALUE).build())); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangAttachedItemTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangAttachedItemTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..be259bc7187a38847f31f1175fa2e5f9391feedf --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangAttachedItemTestFactory.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.ozg.pluto.common.GrpcObject; +import de.itvsh.ozg.pluto.common.GrpcProperty; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItem; + +public class GrpcVorgangAttachedItemTestFactory { + + public final static String ID = UUID.randomUUID().toString(); + public final static String CLIENT = LoremIpsum.getInstance().getWords(1); + public final static String ITEM_NAME = LoremIpsum.getInstance().getWords(1); + public final static String ITEM_KEY = LoremIpsum.getInstance().getWords(1); + public final static String ITEM_VALUE = LoremIpsum.getInstance().getWords(1); + public final static long VERSION = 73; + + public static final GrpcObject ITEM = GrpcObject.newBuilder() + .addProperty(GrpcProperty.newBuilder() + .setName(ITEM_KEY) + .addValue(ITEM_VALUE) + .build()) + .build(); + + public static GrpcVorgangAttachedItem create() { + return createBuilder().build(); + } + + public static GrpcVorgangAttachedItem.Builder createBuilder() { + return GrpcVorgangAttachedItem.newBuilder() + .setId(ID) + .setClient(CLIENT) + .setVorgangId(VorgangHeaderTestFactory.ID) + .setItemName(ITEM_NAME) + .setItem(ITEM) + .setVersion(VERSION); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangHeaderTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangHeaderTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..12eb20fa5a7de3f3f91fe06b50e6bb6c76d8d09d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangHeaderTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.VorgangHeaderTestFactory.*; + +import de.itvsh.ozg.pluto.vorgang.GrpcVorgangHeader; + +public class GrpcVorgangHeaderTestFactory { + + public static GrpcVorgangHeader create() { + return createBuilder().build(); + } + + public static GrpcVorgangHeader.Builder createBuilder() { + return GrpcVorgangHeader.newBuilder() + .setNummer(NUMMER) + .setCreatedAt(CREATED_AT_STR) + .setAktenzeichen(AKTENZEICHEN) + .setAssignedTo(ASSIGNED_TO) + .setName(NAME) + .setStatus(STATUS.name()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d1bee97e6b0fee577dcf8a6665b1c8eb0a356db3 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangTestFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.LocalDateTime; +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangResponse; +import de.itvsh.ozg.pluto.vorgang.GrpcVorgangHeader; + +public class GrpcVorgangTestFactory { + + private static final int TOTAL = 100; + + private static final String ID = UUID.randomUUID().toString(); + private static final String NAME = LoremIpsum.getInstance().getWords(10); + private static final String CREATED_AT = LocalDateTime.now().toString(); + private static final String STATUS = "NEU"; + private static final String NUMMER = "VorgangNummer123"; + + public static GrpcFindVorgangResponse createVorgangResponse() { + return createVorgangResponseBuilder().build(); + } + + private static GrpcFindVorgangResponse.Builder createVorgangResponseBuilder() { + return GrpcFindVorgangResponse.newBuilder() + .setTotal(TOTAL) + .addVorgang(create()); + } + + private static GrpcVorgangHeader create() { + return createBuilder().build(); + } + + private static GrpcVorgangHeader.Builder createBuilder() { + return GrpcVorgangHeader.newBuilder() + .setId(ID) + .setName(NAME) + .setNummer(NUMMER) + .setCreatedAt(CREATED_AT) + .setStatus(STATUS); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1eefbc10c3d95c66d043e06b1fcc116af2e307d4 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangResponseTestFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangWithEingangResponse; + +class GrpcVorgangWithEingangResponseTestFactory { + + static GrpcFindVorgangWithEingangResponse createVorgangWithEingangResponse() { + return createVorgangWithEingangResponseBuilder().build(); + } + + private static GrpcFindVorgangWithEingangResponse.Builder createVorgangWithEingangResponseBuilder() { + return GrpcFindVorgangWithEingangResponse.newBuilder().setVorgangWithEingang(GrpcVorgangWithEingangTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d763fa2f147d8e16398a3f09fdc04423611f2ffa --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcVorgangWithEingangTestFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.VorgangHeaderTestFactory.*; + +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; +import de.itvsh.ozg.pluto.vorgang.GrpcEingang; +import de.itvsh.ozg.pluto.vorgang.GrpcVorgangWithEingang; + +class GrpcVorgangWithEingangTestFactory { + + static GrpcVorgangWithEingang create() { + return createBuilder().build(); + } + + static GrpcVorgangWithEingang createWithEingang(GrpcEingang eingang) { + return createBuilder().setEingang(eingang).build(); + } + + static GrpcVorgangWithEingang.Builder createBuilder() { + return GrpcVorgangWithEingang.newBuilder() + .setStatus(STATUS.name()) + .setNummer(NUMMER) + .setAktenzeichen(AKTENZEICHEN) + .setAssignedTo(ASSIGNED_TO) + .setCreatedAt(CREATED_AT_STR) + .clearClientAttributes() + .addClientAttributes( + GrpcClientAttributeTestFactory.createBuilder() + .setAttributeName(ClientAttributeService.HAS_NEW_POSTFACH_NACHRICHT_ATTRIBUTE_NAME) + .setValue(GrpcClientAttributeValue.newBuilder().setBoolValue(true)).build()) + .setEingang(GrpcEingangTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcZustaendigeStelleTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcZustaendigeStelleTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..908cf76516023d2e64162c607ec19e860268d557 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/GrpcZustaendigeStelleTestFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.ozg.pluto.vorgang.GrpcZustaendigeStelle; + +public class GrpcZustaendigeStelleTestFactory { + private static final String EMAIL = ZustaendigeStelleTestFactory.EMAIL; + private static final String ORGANISATIONSEINHEITEN_ID = ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID; + + public static GrpcZustaendigeStelle create() { + return createBuilder().build(); + } + + public static GrpcZustaendigeStelle.Builder createBuilder() { + return GrpcZustaendigeStelle.newBuilder() + .setEmail(EMAIL) + .setOrganisationseinheitenId(ORGANISATIONSEINHEITEN_ID); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/RedirectRequestTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/RedirectRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..f734412eb1feb5814671ff84a5619cd45b1c5c59 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/RedirectRequestTestFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.vorgang.forwarding.RedirectRequest; +import de.itvsh.kop.common.test.TestUtils; + +public class RedirectRequestTestFactory { + + public static final String EMAIL = "test@ozg-sh.de"; + public static final String PASSWORD_AS_STR = "ValidesPassword"; + public static final char[] PASSWORD = PASSWORD_AS_STR.toCharArray(); + + public static RedirectRequest create() { + return createBuilder().build(); + } + + public static RedirectRequest.RedirectRequestBuilder createBuilder() { + return RedirectRequest.builder() + .email(EMAIL) + .password(PASSWORD); + } + + public static String createRedirectRequestContent(CreateCommand command) { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithRedirectRequest.json.tmpl", + command.getOrder().name(), + TestUtils.addQuote(command.getRedirectRequest().getEmail()), + TestUtils.addQuote(String.valueOf(command.getRedirectRequest().getPassword()))); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangAuthorizationServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangAuthorizationServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b5bed86a7e7c01305ac474171fe7ebb5ea16d1eb --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangAuthorizationServiceTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +class VorgangAuthorizationServiceTest { + + @InjectMocks + private VorgangAuthorizationService service; + @Mock + private CurrentUserService userService; + + @Nested + class TestVerifyByUserRole { + + @Nested + class TestForVerwaltungPoststelle { + + @BeforeEach + void mockUserRole() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(true); + } + + @DisplayName("should return true by vorgangstatus 'Neu'") + @Test + void shouldReturnTrue() { + var authorized = service.verifyByUserRole(VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.NEU).build()); + + assertThat(authorized).isTrue(); + } + + @DisplayName("should return false by other vorgangstatus then 'Neu'") + @ParameterizedTest + @EnumSource(value = VorgangStatus.class, names = { "ANGENOMMEN", "VERWORFEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", + "WEITERGELEITET" }) + void shouldReturnFalse(VorgangStatus status) { + var authorized = service.verifyByUserRole(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + + assertThat(authorized).isFalse(); + } + } + + @Nested + class TestForOtherRoles { + + @BeforeEach + void mockUserRole() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(false); + } + + @DisplayName("should return true on role 'VERWALTUNG_USER' independent which vorgangstatus") + @ParameterizedTest + @EnumSource(value = VorgangStatus.class, names = { "NEU", "ANGENOMMEN", "VERWORFEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", + "WEITERGELEITET" }) + void shouldReturnTrue(VorgangStatus status) { + var authorized = service.verifyByUserRole(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + + assertThat(authorized).isTrue(); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..5102a5f41f730d982776c7c511bc5fc8c055c0e8 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerITCase.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.goofy.common.errorhandling.ResourceNotFoundException; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.postfach.PostfachMailController; +import de.itvsh.goofy.vorgang.forwarding.ForwardingController; +import de.itvsh.goofy.wiedervorlage.WiedervorlageTestFactory; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class VorgangControllerITCase { + + @MockBean + private VorgangRemoteService remoteService; + @MockBean + private CommandController commandController; + @MockBean + private ForwardingController forwardingController; + @MockBean + private PostfachMailController postfachMailController; + @MockBean + private ClientAttributeService clientAttributeService; + + @Autowired + private MockMvc mockMvc; + + @WithMockUser + @Nested + class TestVorgangListByPage { + + private final String PATH = VorgangController.PATH + "/"; + + private final VorgaengeHeaderResponse response = VorgaengeHeaderResponse.builder().vorgaengeHeader(List.of( + VorgangHeaderTestFactory.createBuilder().nextFrist(WiedervorlageTestFactory.FRIST).build())).build(); + + @BeforeEach + void mockRemoteService() { + when(remoteService.findVorgaengeHeader(any())).thenReturn(response); + } + + @Test + void shouldReturnResult() throws Exception { + doRequest().andExpect(jsonPath("$._embedded.vorgangHeaderList[0].nextFrist").value(WiedervorlageTestFactory.FRIST_STR)); + } + + @Test + void shouldReturnStatusOk() throws Exception { + doRequest().andExpect(status().isOk()); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(PATH, VorgangHeaderTestFactory.ID)); + } + } + + @WithMockUser + @Nested + class TestGetSingleVorgang { + + public static final String PATH = VorgangController.PATH + "/{id}"; + + @Test + void shouldReturnNotFound() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())).thenThrow(new ResourceNotFoundException(Vorgang.class, StringUtils.EMPTY)); + + doRequest().andExpect(status().isNotFound()); + } + + @Test + void shouldFormatDateTime() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + + doRequest().andExpect(status().isOk()).andExpect(jsonPath("$.createdAt").value(VorgangHeaderTestFactory.CREATED_AT_STR)); + } + + @Test + void shouldHaveAnnehmenLink() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + + doRequest().andExpect(jsonPath("$._links.annehmen.href") + .value("http://localhost/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/relations/" + VorgangHeaderTestFactory.ID + "/" + + VorgangHeaderTestFactory.VERSION + "/commands")); + } + + @WithMockUser + @Nested + class TestLinkToAssignedUser { + + @Test + void shouldReturnStatusOk() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + + doRequest().andExpect(status().is2xxSuccessful()); + } + + @Test + void shouldBePresentIfAssigned() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + + doRequest() + .andExpect(jsonPath("$._links.assignedTo.href").value("http://localhost:9092/api/userProfiles/" + UserProfileTestFactory.ID)); + } + + @Test + void shouldNOTBePresentIfNOSTAssigned() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())) + .thenReturn(VorgangWithEingangTestFactory.createBuilder().assignedTo(null).build()); + + doRequest().andExpect(jsonPath("$._links.assignedTo.href").doesNotExist()); + } + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(PATH, VorgangHeaderTestFactory.ID)); + } + } + + @Nested + class TestClientAttribute { + @Test + void shouldReturnNoContent() throws Exception { + mockMvc + .perform(put("http://localhost/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"hasNewPostfachNachricht\":false}")) + .andExpect(status().isNoContent()); + } + + @Test + void shouldReturnUnprocessableEntity() throws Exception { + mockMvc + .perform(put("http://localhost/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"hasNewPostfachNachricht\":true}")) + .andExpect(status().isUnprocessableEntity()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9d0dceec2de8dd13d837097fca8dd23ce53c28f7 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangControllerTest.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.EingangTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.hateoas.EntityModel; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.clientattribute.ClientAttributeService; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class VorgangControllerTest { + + private final String RESET_POSTFACH_NACHRICHT_PATH = VorgangController.PATH + "/{0}/hasNewPostfachNachricht"; + private final String PATH = VorgangController.PATH + "/"; + + @Spy + @InjectMocks + private VorgangController controller; + + @Mock + private ClientAttributeService clientAttributeService; + @Mock + private VorgangService vorgangService; + @Mock + private VorgangModelAssembler modelAssembler; + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + ApplicationContext context = mock(ApplicationContext.class); + urlProvider.setApplicationContext(context); + } + + @Nested + class TestVorgangListByPage { + + final int PAGE = 5; + + final int LIMIT = 7; + + @BeforeEach + void initTest() { + when(vorgangService.findVorgaengeHeader(any(FindVorgaengeHeaderRequestCriteria.class))) + .thenReturn(VorgangListResponseTestFactory.create()); + } + + @Test + void shouldCallServiceWithDefaultPage() throws Exception { + mockMvc.perform(get(PATH)).andExpect(status().isOk()); + + verify(controller).buildFindVorgaengeRequestCriteria(0, Optional.empty(), Optional.empty(), Optional.empty()); + } + + @Test + void shouldCallServiceWithParameter() throws Exception { + callEndpointWithParamsPageSearchAndLimit(); + + verify(controller).buildFindVorgaengeRequestCriteria(PAGE, Optional.of("test"), Optional.of(7), + Optional.of(UserProfileTestFactory.ID)); + } + + @Test + void shouldCallService() throws Exception { + callEndpointWithParam(); + + verify(controller).buildFindVorgaengeRequestCriteria(PAGE, Optional.empty(), Optional.empty(), Optional.empty()); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpointWithParam(); + + verify(modelAssembler).toCollectionModel(any(), any(), any()); + } + + private void callEndpointWithParam() throws Exception { + mockMvc.perform(get(PATH).param(VorgangController.PARAM_PAGE, Integer.toString(PAGE))).andExpect(status().isOk()); + } + + private void callEndpointWithParamsPageSearchAndLimit() throws Exception { + mockMvc.perform(get(PATH) + .param(VorgangController.PARAM_PAGE, Integer.toString(PAGE)) + .param(VorgangController.PARAM_SEARCH, "test") + .param(VorgangController.PARAM_LIMIT, Integer.toString(LIMIT)) + .param(VorgangController.PARAM_ASSIGNED_TO, UserProfileTestFactory.ID.toString())) + .andExpect(status().isOk()); + } + } + + @Nested + class TestBuildFindVorgaengeRequestCriteria { + + private final static Integer PAGE = 1; + private final static Optional<String> SEARCH_BY = Optional.of("SuchBegriff"); + private final static Optional<Integer> LIMIT = Optional.of(5); + private final static Optional<UserId> ASSIGNED_TO = Optional.of(UserProfileTestFactory.ID); + + @Test + void shouldHaveSetPage() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getPage()).isEqualTo(PAGE); + } + + @Test + void shouldHaveSetSearchBy() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getSearchBy()).isEqualTo(SEARCH_BY); + } + + @Test + void shouldHaveSetRequestLimit() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getRequestLimit()).isEqualTo(LIMIT); + } + + @Test + void shouldHaveSetAssignedTo() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getAssignedTo()).isEqualTo(ASSIGNED_TO); + } + + @Test + void shouldHaveCalculatedOffset() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getOffset()).isEqualTo(5); + } + + @Test + void shouldHaveSetLimit() { + var requestCriteria = buildRequestCriteria(); + + assertThat(requestCriteria.getLimit()).isEqualTo(5); + } + + private FindVorgaengeHeaderRequestCriteria buildRequestCriteria() { + return controller.buildFindVorgaengeRequestCriteria(PAGE, SEARCH_BY, LIMIT, ASSIGNED_TO); + } + } + + @Nested + class TestSearchVorgangList { + + static final String SEARCH_STRING = "Looking for freedom"; + + @BeforeEach + void initTest() { + when(vorgangService.findVorgaengeHeader(any(FindVorgaengeHeaderRequestCriteria.class))) + .thenReturn(VorgangListResponseTestFactory.create()); + } + + @Test + void shouldReturnOk() throws Exception { + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(vorgangService).findVorgaengeHeader(any(FindVorgaengeHeaderRequestCriteria.class)); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(PATH).param(VorgangController.PARAM_SEARCH, SEARCH_STRING)) + .andExpect(status().is2xxSuccessful()); + } + } + + @Nested + class TestVorgangWithEingang { + + final int page = 0; + + @BeforeEach + void initTest() { + when(vorgangService.findVorgangWithEingang(any())).thenReturn(VorgangWithEingangTestFactory.create()); + when(modelAssembler.toModel(any())).then(i -> EntityModel.of(i.getArgument(0))); + + ApplicationContext context = mock(ApplicationContext.class); + Environment environment = mock(Environment.class); + when(environment.getProperty(anyString())).thenReturn("test/"); + when(context.getEnvironment()).thenReturn(environment); + urlProvider.setApplicationContext(context); + } + + @Test + void shouldCallService() throws Exception { + performRequest(); + + verify(vorgangService).findVorgangWithEingang(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + performRequest(); + + verify(modelAssembler).toModel(any()); + } + + @Nested + class TestDataMapping { + + @DisplayName("Single Field should be in json first level") + @Test + void singleField() throws Exception { + performRequest().andExpect(jsonPath("$.eingang.formData." + SINGLE_FIELD_NAME).value(SINGLE_FIELD_VALUE)); + } + + @DisplayName("Numeric field should be numeric in json") + @Test + void numericField() throws Exception { + performRequest().andExpect(jsonPath("$.eingang.formData." + NUMERIC_FIELD_NAME).isNumber()); + performRequest().andExpect(jsonPath("$.eingang.formData." + NUMERIC_FIELD_NAME).value(NUMERIC_FIELD_VALUE)); + } + + @DisplayName("SubForm should have name as key") + @Test + void keyOfsubForm() throws Exception { + performRequest().andExpect(jsonPath("$.eingang.formData." + SUBFORM_NAME).isMap()); + } + + @DisplayName("SubForm-Field should be present") + @Test + void subFormField() throws Exception { + performRequest().andExpect(jsonPath("$.eingang.formData." + SUBFORM_NAME + "." + SUBFORM_FIELD_NAME).value(SUBFORM_FIELD_VALUE)); + } + + } + + private ResultActions performRequest() throws Exception { + return mockMvc.perform(get(PATH + "/" + VorgangHeaderTestFactory.ID)).andExpect(status().isOk()); + } + } + + @Nested + class TestClientAttribute { + @Test + void resetNewPostfachNachricht() throws Exception { + performRequest("{\"hasNewPostfachNachricht\":false}").andExpect(status().isNoContent()); + + verify(clientAttributeService).resetPostfachNachricht(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldReturnUnprocessableEntity() throws Exception { + performRequest("{\"hasNewPostfachNachricht\":true}").andExpect(status().isUnprocessableEntity()); + + verify(clientAttributeService, never()).resetPostfachNachricht(VorgangHeaderTestFactory.ID); + } + + private ResultActions performRequest(String body) throws Exception { + return mockMvc + .perform(put(RESET_POSTFACH_NACHRICHT_PATH, VorgangHeaderTestFactory.ID).contentType(MediaType.APPLICATION_JSON_VALUE) + .content(body)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..80652a8ee60859d6b20e5a124a3b395e290ce183 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderMapperTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.VorgangHeaderTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import java.time.format.DateTimeFormatter; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.goofy.wiedervorlage.WiedervorlageTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttribute; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcClientAttributeValue; + +class VorgangHeaderMapperTest { + + @Spy + @InjectMocks + private VorgangHeaderMapper mapper = Mappers.getMapper(VorgangHeaderMapper.class); + + @Spy + private UserIdMapper userIdMapper = Mappers.getMapper(UserIdMapper.class); + + @Nested + class TestToVorgangHeader { + + @Test + void shouldMapCreatedAt() { + var header = callMapper(); + + assertThat(header.getCreatedAt()).isEqualTo(CREATED_AT); + } + + @Test + void shouldMapNummer() { + var header = callMapper(); + + assertThat(header.getNummer()).isEqualTo(NUMMER); + } + + @Test + void shouldMapAktenzeichen() { + var header = callMapper(); + + assertThat(header.getAktenzeichen()).isEqualTo(AKTENZEICHEN); + } + + @Test + void shouldMapStatus() { + var header = callMapper(); + + assertThat(header.getStatus()).isEqualTo(VorgangStatus.NEU); + } + + @Test + void shouldMapAssignedTo() { + var header = callMapper(); + + assertThat(header.getAssignedTo()).isEqualTo(UserId.from(ASSIGNED_TO)); + } + + @Nested + class NextFrist { + + @Test + void shouldSetEmptyOptionalIfNotExist() { + var vorgangHeaderWithEmptyClientAttributes = GrpcVorgangHeaderTestFactory.createBuilder().clearClientAttributes().build(); + + var header = mapper.toVorgangHeader(vorgangHeaderWithEmptyClientAttributes); + + assertThat(header.getNextFrist()).isNull(); + } + + @Test + void shouldSetIfExists() { + var nextWiedervorlageFristClientAttribute = GrpcClientAttribute.newBuilder() + .setAttributeName(VorgangHeaderMapper.WIEDERVORLAGE_NEXT_FRIST_ATTRIBUTE_NAME) + .setValue(GrpcClientAttributeValue.newBuilder() + .setStringValue(WiedervorlageTestFactory.FRIST.format(DateTimeFormatter.ISO_DATE)).build()) + .build(); + var vorgangHeaderWithNextWiedervorlageFristAsClientAttribute = GrpcVorgangHeaderTestFactory.createBuilder() + .addClientAttributes(nextWiedervorlageFristClientAttribute).build(); + + var header = mapper.toVorgangHeader(vorgangHeaderWithNextWiedervorlageFristAsClientAttribute); + + assertThat(header.getNextFrist()).isEqualTo(WiedervorlageTestFactory.FRIST); + } + } + + @Nested + class HasPostfachNachricht { + @Test + void shouldSetFalseIfNotExists() { + var vorgangHeader = GrpcVorgangHeaderTestFactory.createBuilder().clearClientAttributes().build(); + + var mapped = mapper.toVorgangHeader(vorgangHeader); + + assertThat(mapped.isHasPostfachNachricht()).isFalse(); + } + + @Test + void shouldBeTrue() { + var clientAttribute = GrpcClientAttributeTestFactory.createWith("hasPostfachNachricht", true); + var vorgangHeader = GrpcVorgangHeaderTestFactory.createBuilder().addClientAttributes(clientAttribute).build(); + + var mapped = mapper.toVorgangHeader(vorgangHeader); + + assertThat(mapped.isHasPostfachNachricht()).isTrue(); + } + } + + @Nested + class HasNewPostfachNachricht { + @Test + void shouldSetFalseIfNotExists() { + var vorgangHeader = GrpcVorgangHeaderTestFactory.createBuilder().clearClientAttributes().build(); + + var mapped = mapper.toVorgangHeader(vorgangHeader); + + assertThat(mapped.isHasNewPostfachNachricht()).isFalse(); + } + + @Test + void shouldBeTrue() { + var clientAttribute = GrpcClientAttributeTestFactory.createWith("hasNewPostfachNachricht", true); + var vorgangHeader = GrpcVorgangHeaderTestFactory.createBuilder().addClientAttributes(clientAttribute).build(); + + var mapped = mapper.toVorgangHeader(vorgangHeader); + + assertThat(mapped.isHasNewPostfachNachricht()).isTrue(); + } + } + + private VorgangHeader callMapper() { + return mapper.toVorgangHeader(GrpcVorgangHeaderTestFactory.create()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..36b9060f5a3523d607a6300edaae1938db886b2d --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangHeaderTestFactory.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +public class VorgangHeaderTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final long VERSION = 42L; + public static final String NAME = LoremIpsum.getInstance().getWords(10); + public static final VorgangStatus STATUS = VorgangStatus.NEU; + public static final String CREATED_AT_STR = "2021-01-10T10:30:00Z"; + public static final ZonedDateTime CREATED_AT = ZonedDateTime.parse(CREATED_AT_STR); + public static final String NUMMER = "VorgangNummerTest123"; + public static final String AKTENZEICHEN = "0123456789XY"; + public static final String ASSIGNED_TO = "paul"; + + public static VorgangHeader create() { + return createBuilder().build(); + } + + public static VorgangHeader.VorgangHeaderBuilder createBuilder() { + return VorgangHeader.builder() + .id(ID) + .version(VERSION) + .name(NAME) + .nummer(NUMMER) + .status(STATUS) + .createdAt(CREATED_AT) + .assignedTo(UserProfileTestFactory.ID); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..efc638ec94b067d5d6ce196cca05279649993fd0 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangITCase.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.postfach.PostfachMailController; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class VorgangITCase { + + @MockBean + private VorgangRemoteService remoteService; + + @MockBean + private CommandController commandController; + + @MockBean + private CurrentUserService userService; + + @MockBean + private PostfachMailController postfachController; + + @Autowired + private MockMvc mockMvc; + + @WithMockUser + @Nested + class TestGetVorgangWithEingang { + + static final String SINGLE_PATH = VorgangController.PATH + "/{id}"; + + @BeforeEach + void mockStub() { + when(postfachController.isPostfachConfigured()).thenReturn(true); + when(remoteService.findVorgangWithEingang(anyString())).thenReturn(VorgangWithEingangTestFactory.create()); + } + + @Test + void shouldReturnVorgangOnMatchingOrganisationseinheitId() throws Exception { + when(userService.getUser()).thenReturn( + UserProfileTestFactory.createBuilder().clearOrganisationseinheitIds() + .organisationseinheitId(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID).build()); + + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldReturnVorgangOnEmptyUserOrganisationseinheitIdList() throws Exception { + when(userService.getUser()).thenReturn(UserProfileTestFactory.createBuilder().clearOrganisationseinheitIds().build()); + + doRequest().andExpect(status().isOk()); + } + + @Nested + class TestAuthorizationByRole { + + @Nested + class TestForVerwaltungPoststelle { + + @BeforeEach + void mockUserService() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(true); + } + + @DisplayName("should return http forbidden status if vorgangstatus is different to 'Neu'") + @ParameterizedTest + @EnumSource(value = VorgangStatus.class, names = { "ANGENOMMEN", "VERWORFEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", + "WEITERGELEITET" }) + void shouldReturnForbiddenStatus(VorgangStatus status) throws Exception { + when(remoteService.findVorgangWithEingang(anyString())) + .thenReturn(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + + doRequest().andExpect(status().isForbidden()); + } + + @DisplayName("should return http ok status if vorgangstatus is equal 'Neu'") + @Test + void shouldReturnOkStatus() throws Exception { + when(remoteService.findVorgangWithEingang(anyString())) + .thenReturn(VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.NEU).build()); + + doRequest().andExpect(status().isOk()); + } + } + + @Nested + class TestForOtherRoles { + + @BeforeEach + void mockUserRole() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(false); + } + + @DisplayName("should return true on role 'VERWALTUNG_USER' independent which vorgangstatus") + @ParameterizedTest + @EnumSource(value = VorgangStatus.class, names = { "NEU", "ANGENOMMEN", "VERWORFEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", + "WEITERGELEITET" }) + void shouldReturnTrue(VorgangStatus status) throws Exception { + when(remoteService.findVorgangWithEingang(anyString())) + .thenReturn(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + + doRequest().andExpect(status().isOk()); + } + } + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(SINGLE_PATH, VorgangHeaderTestFactory.ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangListResponseTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangListResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1baea8b849ddd81f97b12ecc49082d9ab3a883b0 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangListResponseTestFactory.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.thedeanda.lorem.LoremIpsum; + +public class VorgangListResponseTestFactory { + + public static final int TOTAL = 300; + + public static VorgaengeHeaderResponse create() { + return createBuilder().build(); + } + + public static VorgaengeHeaderResponse.VorgaengeHeaderResponseBuilder createBuilder() { + return VorgaengeHeaderResponse.builder() + .total(TOTAL) + .vorgaengeHeader(createList()); + } + + public static List<VorgangHeader> createList() { + return IntStream.range(0, 10) + .mapToObj(i -> VorgangHeaderTestFactory.createBuilder().name(i + LoremIpsum.getInstance().getName()).build()) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e7cbe0567a9411b9705362c259cd356000dafbf9 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangModelAssemblerTest.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.common.UserProfileUrlProviderTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.CollectionModel; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +class VorgangModelAssemblerTest { + + private final static String BASE_PATH = VorgangController.PATH; + + @InjectMocks + private VorgangModelAssembler modelAssembler; + @Mock + private CurrentUserService userService; + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + @Nested + class TestCollectionModel { + + private FindVorgaengeHeaderRequestCriteria.FindVorgaengeHeaderRequestCriteriaBuilder requestCriteriaBuilder = FindVorgaengeHeaderRequestCriteria + .builder() + .requestLimit(Optional.empty()) + .limit(0) + .searchBy(Optional.empty()) + .assignedTo(Optional.empty()); + + private VorgaengeHeaderResponse response = VorgaengeHeaderResponse.builder() + .vorgaengeHeader(Collections.emptyList()) + .total(VorgangController.VORGANG_PAGE_SIZE * 2) + .build(); + + @Test + void shouldHaveSize() throws Exception { + initUserProfileUrlProvider(urlProvider); + + var requestCriteria = requestCriteriaBuilder.page(1).requestLimit(Optional.of(1)).build(); + var response = VorgaengeHeaderResponse.builder().vorgaengeHeader(List.of()).total(VorgangController.VORGANG_PAGE_SIZE).build(); + + var model = toCollectionModel(requestCriteria, response); + + assertThat(model.getContent()).hasSize(1); + } + + @Nested + class TestNextLink { + + @BeforeEach + void prepareBuilder() { + requestCriteriaBuilder.page(1); + + initUserProfileUrlProvider(urlProvider); + } + + @Test + void shouldContainsLimiParameter() { + var requestCriteria = requestCriteriaBuilder.requestLimit(Optional.of(7)).build(); + + var link = getNextLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=2&limit=7"); + } + + @Test + void shouldContainsSearchByParameter() { + var requestCriteria = requestCriteriaBuilder.searchBy(Optional.of("test")).build(); + + var link = getNextLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=2&searchBy=test"); + } + + @Test + void shouldContainsAssignedToParameter() { + var requestCriteria = requestCriteriaBuilder.assignedTo(Optional.of(UserProfileTestFactory.ID)).build(); + + var link = getNextLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo(BASE_PATH + "?page=2&assignedTo=" + UserProfileTestFactory.ID.toString()); + } + + private Optional<Link> getNextLinkByRequest(FindVorgaengeHeaderRequestCriteria requestCriteria) { + return toCollectionModel(requestCriteria, response).getLink(VorgangModelAssembler.REL_NEXT); + } + } + + @Nested + class TestPreviousLink { + + @BeforeEach + void prepareBuilder() { + requestCriteriaBuilder.page(2); + } + + @Test + void shouldHavePreviousLink() { + var requestCriteria = requestCriteriaBuilder.requestLimit(Optional.empty()).build(); + + var link = getPrevLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=1"); + } + + @Test + void shouldContainsLimiParameter() { + var requestCriteria = requestCriteriaBuilder.requestLimit(Optional.of(7)).build(); + + var link = getPrevLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=1&limit=7"); + } + + @Test + void shouldContainsSearchByParameter() { + var requestCriteria = requestCriteriaBuilder.searchBy(Optional.of("test")).build(); + + var link = getPrevLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=1&searchBy=test"); + } + + @Test + void shouldContainsAssignedToParameter() { + var requestCriteria = requestCriteriaBuilder.assignedTo(Optional.of(UserProfileTestFactory.ID)).build(); + + var link = getPrevLinkByRequest(requestCriteria); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "?page=1&assignedTo=" + UserProfileTestFactory.ID); + } + + private Optional<Link> getPrevLinkByRequest(FindVorgaengeHeaderRequestCriteria requestCriteria) { + return toCollectionModel(requestCriteria, response).getLink(VorgangModelAssembler.REL_PREVIOUS); + } + } + + private CollectionModel<EntityModel<Vorgang>> toCollectionModel(FindVorgaengeHeaderRequestCriteria requestCriteria, + VorgaengeHeaderResponse response) { + return modelAssembler.toCollectionModel(Stream.of(VorgangHeaderTestFactory.create()), response, requestCriteria); + } + } + + @Nested + class TestLinksOnModel { + + @Test + void shouldHaveSelfLink() { + var link = toModel().getLink(IanaLinkRelations.SELF_VALUE); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "/" + VorgangHeaderTestFactory.ID); + } + + @Test + void shouldHaveVorgangMitEingangLink() { + var link = toModel().getLink(VorgangModelAssembler.REL_VORGANG_MIT_EINGANG); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(BASE_PATH + "/" + VorgangHeaderTestFactory.ID); + } + + @DisplayName("Without any roles") + @Nested + class WithoutRoles { + + @Test + void shouldNotHaveWiedervorlagenLink() { + var link = toModel().getLink(VorgangModelAssembler.REL_WIEDERVORLAGEN); + + assertThat(link).isEmpty(); + } + + @DisplayName("Should not have Vorgang-assign-links") + @Test + void shouldNotHaveAssignLink() { + var link = toModel().getLink(VorgangModelAssembler.REL_VORGANG_ASSIGN); + + assertThat(link).isEmpty(); + } + } + + @Nested + class ForRoleVerwaltungPoststelle { + + @Test + void shouldHaveAssignLink() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(true); + when(userService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(false); + + var link = toModel().getLink(VorgangModelAssembler.REL_VORGANG_ASSIGN); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(buildCommandLink()); + } + } + + @Nested + class ForRoleVerwaltungUser { + + @BeforeEach + void mockUserService() { + when(userService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(true); + } + + @Test + void shouldHaveWiedervorlagenLink() { + var link = toModel().getLink(VorgangModelAssembler.REL_WIEDERVORLAGEN); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/wiedervorlages?vorgangId=" + VorgangHeaderTestFactory.ID); + } + + @Test + void shouldHaveAssignLink() { + var link = toModel().getLink(VorgangModelAssembler.REL_VORGANG_ASSIGN); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(buildCommandLink()); + } + } + + private String buildCommandLink() { + return String.format("/api/vorgangs/%s/relations/%s/%s/commands", VorgangHeaderTestFactory.ID, + VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION); + } + + private EntityModel<Vorgang> toModel() { + return modelAssembler.toModel(VorgangWithEingangTestFactory.create()); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5b8926d629a2ecb3b75661683924db961ab100a1 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangRemoteServiceTest.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.ozg.pluto.vorgang.GrpcFilterBy; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangRequest; +import de.itvsh.ozg.pluto.vorgang.GrpcFindVorgangResponse; +import de.itvsh.ozg.pluto.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub; + +class VorgangRemoteServiceTest { + + @Spy + @InjectMocks // NOSONAR + private VorgangRemoteService service; + + @Mock + private VorgangServiceBlockingStub serviceStub; + @Mock + private VorgangHeaderMapper vorgangHeaderMapper; + @Mock + private VorgangWithEingangMapper vorgangWithEingangMapper; + @Mock + private CurrentUserService userService; + + private final GrpcFilterBy filterBy = GrpcFilterByTestFactory.create(); + + @Nested + class TestFindVorgaengeHeader { + + @Nested + class TestCalls { + + private final FindVorgaengeHeaderRequestCriteria requestCriteria = FindVorgaengeRequestCriteriaTestFactory.create(); + + @BeforeEach + void initMockReturnValues() { + doReturn(filterBy).when(service).createFilterBy(any()); + doReturn(VorgangListResponseTestFactory.create()).when(service).buildVorgaengeHeaderResponse(any()); + } + + @Test + void shouldCallBuildRequest() { + callService(); + + verify(service).buildFindVorgangRequest(requestCriteria); + } + + @Test + void shouldCallCreateFilterBy() { + callService(); + + verify(service).createFilterBy(requestCriteria.getAssignedTo()); + } + + @Test + void shouldCallStub() { + callService(); + + verify(serviceStub).findVorgang(any()); + } + + private VorgaengeHeaderResponse callService() { + return service.findVorgaengeHeader(requestCriteria); + } + } + + @Nested + class TestBuildFindVorgangRequest { + + @BeforeEach + void initMockReturnValues() { + doReturn(filterBy).when(service).createFilterBy(any()); + } + + @Test + void shouldSetLimit() { + var request = callService(); + + assertThat(request.getLimit()).isEqualTo(FindVorgaengeRequestCriteriaTestFactory.LIMIT); + } + + @Test + void shouldSetOffset() { + var request = callService(); + + assertThat(request.getOffset()).isEqualTo(FindVorgaengeRequestCriteriaTestFactory.OFFSET); + } + + @Test + void shouldSetFilterBy() { + var request = callService(); + + assertThat(request.getFilterBy()).isEqualTo(filterBy); + } + + @Test + void shouldSetOrderBy() { + var request = callService(); + + assertThat(request.getOrderBy().name()).isEqualTo(FindVorgaengeRequestCriteriaTestFactory.ORDER_BY.name()); + } + + private GrpcFindVorgangRequest callService() { + return service.buildFindVorgangRequest(FindVorgaengeRequestCriteriaTestFactory.create()); + } + } + + @Nested + class TestResponse { + + private final GrpcFindVorgangResponse findVorgangRespone = GrpcVorgangTestFactory.createVorgangResponse(); + private final VorgangHeader vorgangHeader = VorgangHeaderTestFactory.create(); + + @BeforeEach + void initMockReturnValues() { + when(vorgangHeaderMapper.toVorgangHeader(any())).thenReturn(vorgangHeader); + } + + @Test + void shouldReturnMappedVorgangHeader() { + var vorgangResponse = callService(); + + assertThat(vorgangResponse.getVorgaengeHeader()).hasSize(1); + assertThat(vorgangResponse.getVorgaengeHeader().get(0)).isEqualTo(vorgangHeader); + } + + @Test + void shouldReturnTotal() { + var vorgangResponse = callService(); + + assertThat(vorgangResponse.getTotal()).isEqualTo(findVorgangRespone.getTotal()); + } + + @Test + void shouldMapVorgangHeader() { + callService(); + + verify(vorgangHeaderMapper).toVorgangHeader(any()); + } + + private VorgaengeHeaderResponse callService() { + return service.buildVorgaengeHeaderResponse(findVorgangRespone); + } + } + } + + @Nested + class TestCreateFilterBy { + + @Nested + class TestAssignedTo { + + @BeforeEach + void mockUserService() { + when(userService.hasRole(any())).thenReturn(false); + when(userService.getUser()).thenReturn(UserProfileTestFactory.create()); + } + + @Test + void shouldBeSetIfExists() { + var filterCriteria = callService(Optional.of(UserProfileTestFactory.ID)); + + assertThat(filterCriteria.getAssignedTo()).isEqualTo(UserProfileTestFactory.ID.toString()); + } + + @Test + void shouldNotBeSetIfNotExist() { + var filterCriteria = callService(Optional.empty()); + + assertThat(filterCriteria.getAssignedTo()).isEqualTo(StringUtils.EMPTY); + + } + } + + @Nested + class TestForPoststelle { + + @BeforeEach + void mockUser() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(true); + } + + @Test + void shouldFillFilterByOrganisationseinheiten() { + var filterBy = callService(); + + assertThat(filterBy.getFilterByOrganisationseinheitenId()).isFalse(); + } + + @Test + void shouldFillStatus() { + var filterBy = callService(); + + assertThat(filterBy.getStatus(0)).isEqualTo(VorgangStatus.NEU.name()); + } + } + + @Nested + class TestForEa { + + @BeforeEach + void mockUser() { + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(false); + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(true); + } + + @Test + void shouldFillFilterByOrganisationseinheiten() { + var filterBy = callService(); + + assertThat(filterBy.getFilterByOrganisationseinheitenId()).isFalse(); + } + } + + @Nested + class TestCreateForOtherRoles { + + @BeforeEach + void mockUser() { + when(userService.hasRole(anyString())).thenReturn(false); + } + + @Test + void shouldCallUserService() { + when(userService.getUser()).thenReturn(UserProfileTestFactory.create()); + + callService(); + + verify(userService).getUser(); + } + + @Test + void shouldFillFilterByOrganisationseinheitenId() { + when(userService.getUser()).thenReturn(UserProfileTestFactory.create()); + + var filterBy = callService(); + + assertThat(filterBy.getFilterByOrganisationseinheitenId()).isTrue(); + } + + @Test + void shouldFillOrganisationseinheitenId() { + when(userService.getUser()).thenReturn(UserProfileTestFactory.create()); + + var filterBy = callService(); + + assertThat(filterBy.getOrganisationseinheitIdList()).containsExactly(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); + } + } + + private GrpcFilterBy callService() { + return callService(Optional.empty()); + } + + private GrpcFilterBy callService(Optional<UserId> assignedTo) { + return service.createFilterBy(assignedTo); + } + } + + @Nested + class TestFindVorgangWithEingang { + + @Nested + class TestCalls { + + private final VorgangWithEingang vorgangWithEingang = VorgangWithEingangTestFactory.create(); + + @BeforeEach + void mockReturnValue() { + when(serviceStub.findVorgangWithEingang(any())) + .thenReturn(GrpcVorgangWithEingangResponseTestFactory.createVorgangWithEingangResponse()); + when(vorgangWithEingangMapper.toVorgangWithEingang(any())).thenReturn(vorgangWithEingang); + doReturn(filterBy).when(service).createFilterBy(any()); + } + + @Test + void shouldCreateFilterBy() { + callService(); + + verify(service).createFilterBy(any()); + } + + @Test + void shouldCallBuildRequest() { + callService(); + + verify(service).buildFindVorgangWithEingangRequest(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallStub() { + callService(); + + verify(serviceStub).findVorgangWithEingang(any()); + } + + @Test + void shouldCallMapper() { + callService(); + + verify(vorgangWithEingangMapper).toVorgangWithEingang(any()); + } + + @Test + void shouldReturn() { + var result = callService(); + + assertThat(result).isEqualTo(vorgangWithEingang); + } + } + + @Nested + class TestBuildRequest { + + @BeforeEach + void mockReturnValue() { + doReturn(filterBy).when(service).createFilterBy(any()); + } + + @Test + void shouldHaveFilter() { + var filter = service.buildFindVorgangWithEingangRequest(VorgangHeaderTestFactory.ID); + + assertThat(filter.getFilterBy()).isNotNull(); + } + + @Test + void shouldContainsVorgangId() { + var filter = service.buildFindVorgangWithEingangRequest(VorgangHeaderTestFactory.ID); + + assertThat(filter.getId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + } + + private VorgangWithEingang callService() { + return service.findVorgangWithEingang(VorgangHeaderTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..08da2a3ac1a5e187451a014cedc3d5c21db014bb --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangServiceTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; + +class VorgangServiceTest { + + @Spy + @InjectMocks // NOSONAR + private VorgangService service; + + @Mock + private VorgangRemoteService remoteService; + @Mock + private CurrentUserService userService; + + @Nested + class TestFindVorgaengeHeader { + + private final FindVorgaengeHeaderRequestCriteria requestCriteria = FindVorgaengeRequestCriteriaTestFactory.create(); + + @Test + void shouldCallRemoteService() { + doReturn(requestCriteria).when(service).setOrderBy(any()); + + service.findVorgaengeHeader(requestCriteria); + + verify(remoteService).findVorgaengeHeader(requestCriteria); + } + + @Nested + class TestOrderBy { + + @Test + void shouldSetPriority() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(false); + + var request = service.setOrderBy(FindVorgaengeRequestCriteriaTestFactory.create()); + + assertThat(request.getOrderBy()).isEqualTo(OrderBy.PRIORITY); + } + + @Test + void shouldSetEaPriority() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(true); + + var request = service.setOrderBy(FindVorgaengeRequestCriteriaTestFactory.create()); + + assertThat(request.getOrderBy()).isEqualTo(OrderBy.EA_PRIORITY); + } + } + } + + @Nested + class TestFindVorgangWithEingang { + + @Test + void shouldCallRemoteService() { + service.findVorgangWithEingang(VorgangHeaderTestFactory.ID); + + verify(remoteService).findVorgangWithEingang(VorgangHeaderTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..0e0d676516192edc0238457f3b3ce7368f7f4b68 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperITCase.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import de.itvsh.ozg.pluto.vorgang.GrpcEingang; +import de.itvsh.ozg.pluto.vorgang.GrpcFormData; +import de.itvsh.ozg.pluto.vorgang.GrpcFormField; + +@SpringBootTest +class VorgangWithEingangMapperITCase { + + @Autowired + private VorgangWithEingangMapper mapper; + + @Nested + class TestToVorgangWithEingang { + + @Nested + class TestMappingFormData { + + @Test + void shouldMapListOfStrings() { + var vorgang = mapper.toVorgangWithEingang(GrpcVorgangWithEingangTestFactory.createWithEingang(buildEingang())); + + assertThat(vorgang.getEingang().getFormData()).containsEntry("TEST", List.of("VALUE1", "VALUE2")); + } + + private GrpcEingang buildEingang() { + return GrpcEingangTestFactory.createBuilder().setFormData(GrpcFormData.newBuilder() + .addField(GrpcFormField.newBuilder().setName("TEST").setValue("VALUE1").build()) + .addField(GrpcFormField.newBuilder().setName("TEST").setValue("VALUE2").build())) + .build(); + } + + } + + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..371f6c1b110a2d375d83129d3a3bec0e5b2729da --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangMapperTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.VorgangHeaderTestFactory.*; +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.BaseTypesMapper; +import de.itvsh.goofy.common.user.UserId; +import de.itvsh.goofy.common.user.UserIdMapper; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +class VorgangWithEingangMapperTest { + + @Spy + @InjectMocks + private VorgangWithEingangMapper mapper = Mappers.getMapper(VorgangWithEingangMapper.class); + + @Spy + private UserIdMapper userIdMapper = Mappers.getMapper(UserIdMapper.class); + + @Spy + private BaseTypesMapper baseTypesMapper = Mappers.getMapper(BaseTypesMapper.class); + + @Mock + private EingangMapper eingangMapper; + + @Nested + class TestGrpcVorgangWithEingangToGoofyVorgangWithEingang { + @Test + void shouldMapCreatedAt() { + var vorgang = callMapper(); + + assertThat(vorgang.getCreatedAt()).isEqualTo(CREATED_AT); + } + + @Test + void shouldMapNummer() { + var vorgang = callMapper(); + + assertThat(vorgang.getNummer()).isEqualTo(NUMMER); + } + + @Test + void shouldMapAktenzeichen() { + var vorgang = callMapper(); + + assertThat(vorgang.getAktenzeichen()).isEqualTo(AKTENZEICHEN); + } + + @Test + void shouldMapStatus() { + var vorgang = callMapper(); + + assertThat(vorgang.getStatus()).isEqualTo(VorgangStatus.NEU); + } + + @Test + void shouldMapAssignedTo() { + var vorgang = callMapper(); + + assertThat(vorgang.getAssignedTo()).isEqualTo(UserId.from(ASSIGNED_TO)); + } + + @Test + void shouldMapClientAttributeHasNewPostfachNachricht() { + var vorgang = callMapper(); + + assertThat(vorgang.isHasNewPostfachNachricht()).isTrue(); + } + + private VorgangWithEingang callMapper() { + return mapper.toVorgangWithEingang(GrpcVorgangWithEingangTestFactory.create()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0553ea33ebe08897b49b45f4f034622a501183a4 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangProzessorTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.common.UserProfileUrlProviderTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.postfach.PostfachMailController; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; + +class VorgangWithEingangProzessorTest { + + @InjectMocks + private VorgangWithEingangProzessor processor; + @Mock + private PostfachMailController postfachMailController; + @Mock + private CurrentUserService userService; + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + @BeforeEach + void init() { + initUserProfileUrlProvider(urlProvider); + } + + @Nested + class TestAttachmentsLink { + + private final LinkRelation linkRel = VorgangWithEingangProzessor.REL_ATTACHMENTS; + private final String PATH = "/api/attachments?eingangId=" + EingangTestFactory.ID; + + @DisplayName("should be present on numberOfAttachments > 0") + @Test + void shouldBePresentByAttachments() { + var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } + + private EntityModel<VorgangWithEingang> buildModelWithAttachments(int numberOfAttachments) { + return buildModelWithEingang(EingangTestFactory.createBuilder() + .numberOfAttachments(numberOfAttachments) + .build()); + } + + private EntityModel<VorgangWithEingang> buildModelWithEingang(Eingang eingang) { + return EntityModel.of(VorgangWithEingangTestFactory.createBuilder().eingang(eingang).build()); + } + } + + @Nested + class TestRepresentationsLink { + + private final LinkRelation linkRel = VorgangWithEingangProzessor.REL_REPRESENTATIONS; + private final String PATH = "/api/representations?eingangId=" + EingangTestFactory.ID; + + @DisplayName("should be present on numberOfRepresentations > 0") + @Test + void shouldBePresentByRepresentations() { + var link = processor.process(buildModelWithRepresentation(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithRepresentation(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } + + private EntityModel<VorgangWithEingang> buildModelWithRepresentation(int numberOfRepresentations) { + return buildModelWithEingang(EingangTestFactory.createBuilder() + .numberOfRepresentations(numberOfRepresentations) + .build()); + } + + private EntityModel<VorgangWithEingang> buildModelWithEingang(Eingang eingang) { + return EntityModel.of(VorgangWithEingangTestFactory.createBuilder().eingang(eingang).build()); + } + } + + @Nested + class TestPostfachMailsLink { + + private final EntityModel<VorgangWithEingang> model = EntityModel.of(VorgangWithEingangTestFactory.create()); + + @Test + void shouldCallPostfachMailController() { + processor.process(model).getLink(VorgangWithEingangProzessor.REL_POSTFACH_MAILS); + + verify(postfachMailController).isPostfachConfigured(); + } + + @Test + void shouldBePresentIfConfigured() { + when(postfachMailController.isPostfachConfigured()).thenReturn(true); + + var link = processor.process(model).getLink(VorgangWithEingangProzessor.REL_POSTFACH_MAILS); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo("/api/postfachMails?vorgangId=" + VorgangHeaderTestFactory.ID); + } + + @Test + void shouldNotBePresentIfNotConfigured() { + when(postfachMailController.isPostfachConfigured()).thenReturn(false); + + var link = processor.process(model).getLink(VorgangWithEingangProzessor.REL_POSTFACH_MAILS); + + assertThat(link).isEmpty(); + } + } + + @Nested + class TestForwardingLink { + + @DisplayName("should NOT be present in other Role then EA") + @ParameterizedTest + @EnumSource + void shouldNotBePresentWithoutRole(Vorgang.VorgangStatus status) { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(false); + + var link = processor.process(buildVorgangInStatus(status)).getLink(VorgangWithEingangProzessor.REL_VORGANG_FORWARDING); + + assertThat(link).isEmpty(); + } + + @DisplayName("with role EinheitlicherAnsprechpartner") + @Nested + class TestWithRoleEinheitlicherAnsprechpartner { + @BeforeEach + void init() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(true); + } + + @DisplayName("should be present in any Status") + @ParameterizedTest + @EnumSource() + void shouldBePresent(VorgangStatus status) { + var link = processor.process(buildVorgangInStatus(status)).getLink(VorgangWithEingangProzessor.REL_VORGANG_FORWARDING); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo("/api/forwardings?vorgangId=" + VorgangHeaderTestFactory.ID); + } + } + + private EntityModel<VorgangWithEingang> buildVorgangInStatus(VorgangStatus status) { + return EntityModel.of(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + } + } + + @DisplayName("Historie Link") + @Nested + class TestHistorieLink { + + @Test + void shouldBePresent() { + var link = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())).getLink(VorgangWithEingangProzessor.REL_HISTORIE); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo("/api/histories?vorgangId=" + VorgangHeaderTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..ff4cf21e4522a3fdcbdfddd554b700192e891278 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/VorgangWithEingangTestFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import static de.itvsh.goofy.vorgang.VorgangHeaderTestFactory.*; + +import de.itvsh.goofy.common.user.UserProfileTestFactory; + +public class VorgangWithEingangTestFactory { + + public static VorgangWithEingang create() { + return createBuilder().build(); + } + + public static VorgangWithEingang.VorgangWithEingangBuilder createBuilder() { + return VorgangWithEingang.builder() + .id(ID) + .version(VERSION) + .assignedTo(UserProfileTestFactory.ID) + .name(NAME) + .status(STATUS) + .nummer(NUMMER) + .createdAt(CREATED_AT) + .hasNewPostfachNachricht(true) + .eingang(EingangTestFactory.create()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..febe1054c00f0ce5cd976bd40bcfedc73d92e4c5 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleMapperTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import static org.assertj.core.api.Assertions.*; + +public class ZustaendigeStelleMapperTest { + private final ZustaendigeStelleMapper mapper = Mappers.getMapper(ZustaendigeStelleMapper.class); + + @Nested + class TestMapToZustaendigeStelle { + + @Test + void shouldMap() { + var zustaendigeStelle = mapper.mapToZustaendigeStelle(GrpcZustaendigeStelleTestFactory.create()); + assertThat(zustaendigeStelle).usingRecursiveComparison().isEqualTo(ZustaendigeStelleTestFactory.create()); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..54638b39f020d1584fa73b90371d24c89c4e17af --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/ZustaendigeStelleTestFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang; + +public class ZustaendigeStelleTestFactory { + public static final String EMAIL = "test@test.de"; + public static final String ORGANISATIONSEINHEITEN_ID = "123456"; + + public static ZustaendigeStelle create() { + return createBuilder().build(); + } + + public static ZustaendigeStelle.ZustaendigeStelleBuilder createBuilder() { + return ZustaendigeStelle.builder() + .email(EMAIL) + .organisationseinheitenId(ORGANISATIONSEINHEITEN_ID); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..87346990ae04e57b666bed06a22bf087d409d7de --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangCommandProzessorTest.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.command; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; + +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class VorgangCommandProzessorTest { + + private static final String EXPECTED_COMMAND_LINK_TEMPL = "/api/vorgangs/%s/relations/%s/%d/commands"; + private static final String EXPECTED_COMMAND_LINK = String.format(EXPECTED_COMMAND_LINK_TEMPL, CommandTestFactory.VORGANG_ID, + VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION); + + @Spy + @InjectMocks + private VorgangCommandProzessor processor; + @Mock + private CurrentUserService userService; + + @Nested + @DisplayName("add 'annehmen' link") + class TestAnnehmenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_ANNEHMEN; + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.NEU)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "NEU" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'verwerfen' link") + class TestVerwerfenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_VERWERFEN; + + @ParameterizedTest + @ValueSource(strings = { "EINHEITLICHER_ANSPRECHPARTNER", "VERWALTUNG_USER" }) + void shouldBePresentAtStatusAndRole(String userRole) { + doReturn(userRole).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(VorgangStatus.NEU)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "NEU" }) + void shouldNOTbePresentAtStatusAndRoleEa(VorgangStatus status) { + doReturn(UserRole.EINHEITLICHER_ANSPRECHPARTNER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "NEU" }) + void shouldNOTbePresentAtStatusAndRoleVerwaltungUser(VorgangStatus status) { + doReturn(UserRole.VERWALTUNG_USER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'zurueckholen' link") + class TestZurueckholenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_ZURUECKHOLEN; + + @ParameterizedTest + @ValueSource(strings = { "EINHEITLICHER_ANSPRECHPARTNER", "VERWALTUNG_USER" }) + void shouldBePresentAtStatusAndRole(String userRole) { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.VERWORFEN)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(names = { "NEU", "ANGENOMMEN" }) + void shouldNOTbePresentAtStatusAndRoleVerwaltungUser(VorgangStatus status) { + doReturn(UserRole.VERWALTUNG_USER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + + @ParameterizedTest + @EnumSource(names = { "NEU", "ANGENOMMEN" }) + void shouldNOTbePresentAtStatusAndRoleEa(VorgangStatus status) { + doReturn(UserRole.EINHEITLICHER_ANSPRECHPARTNER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'bearbeiten' link") + class TestBearbeitenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_BEARBEITEN; + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.ANGENOMMEN)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref) + .isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "ANGENOMMEN", "ABGESCHLOSSEN" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'bescheiden' link") + class TestBescheidenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_BESCHEIDEN; + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.IN_BEARBEITUNG)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref) + .isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "IN_BEARBEITUNG" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'zurueckstellen' link") + class TestZurueckstellenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_ZURUECKSTELLEN; + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.IN_BEARBEITUNG)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "IN_BEARBEITUNG" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + @DisplayName("add 'abschliessen' link") + class TestAbschliessenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_ABSCHLIESSEN; + + @Nested + class TestOnVerwaltungUser { + + @BeforeEach + void mockUserRole() { + doReturn(UserRole.VERWALTUNG_USER).when(processor).getUserRole(); + } + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.BESCHIEDEN)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref) + .isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "BESCHIEDEN" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + class TestOnEinheitlicherAnsprechpartner { + + @BeforeEach + void mockUserRole() { + doReturn(UserRole.EINHEITLICHER_ANSPRECHPARTNER).when(processor).getUserRole(); + } + + @Test + void shouldBePresent() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.IN_BEARBEITUNG)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref) + .isEqualTo(EXPECTED_COMMAND_LINK); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "IN_BEARBEITUNG" }) + void shouldNOTbePresentbyStatus(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + } + + @Nested + @DisplayName("add 'wiedereroeffnen' link") + class TestWiedereroeffnenLink { + + private final LinkRelation linkRel = VorgangCommandProzessor.REL_VORGANG_WIEDEREROEFFNEN; + + @DisplayName("on Vorgang with Status Abgeschlossen") + @ParameterizedTest + @ValueSource(strings = { "EINHEITLICHER_ANSPRECHPARTNER", "VERWALTUNG_USER" }) + void shouldBePresentOnStatusAbgeschlossen(String userRole) { + doReturn(userRole).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(VorgangStatus.ABGESCHLOSSEN)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @Test + @DisplayName("on Vorgang with Status Beschieden and Role VerwaltungUser") + void shouldBePresentOnStatusBeschieden() { + var processed = processor.process(buildVorgangInStatus(VorgangStatus.BESCHIEDEN)); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref).isEqualTo(EXPECTED_COMMAND_LINK); + } + + @Test + @DisplayName("on Vorgang with Status Beschieden and Role EA") + void shouldBeNotPresentOnStatusBeschiedenAndRoleEA() { + doReturn(UserRole.EINHEITLICHER_ANSPRECHPARTNER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(VorgangStatus.BESCHIEDEN)); + + assertThat(processed.getLink(linkRel)).isNotPresent(); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "ABGESCHLOSSEN", "BESCHIEDEN" }) + void shouldNOTbePresentbyStatusAndRoleVerwaltungUser(VorgangStatus status) { + doReturn(UserRole.VERWALTUNG_USER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "ABGESCHLOSSEN", "BESCHIEDEN" }) + void shouldNOTbePresentbyStatusAndRoleEA(VorgangStatus status) { + doReturn(UserRole.EINHEITLICHER_ANSPRECHPARTNER).when(processor).getUserRole(); + + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + } + + @Nested + class TestRoleVerwaltungPoststelle { + @BeforeEach + void init() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(Boolean.FALSE); + when(userService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(Boolean.TRUE); + } + + @ParameterizedTest + @EnumSource() + void shouldNotBePresent(VorgangStatus status) { + var processed = processor.process(buildVorgangInStatus(status)); + + assertThat(processed.getLinks()).isEmpty(); + } + } + + private EntityModel<Vorgang> buildVorgangInStatus(VorgangStatus status) { + return EntityModel.of(VorgangHeaderTestFactory.createBuilder().status(status).build()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e3f275c75f4e379a1b8a6a2536430a7b112bd5e1 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/command/VorgangWithEingangCommandProzessorTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.command; + +import static de.itvsh.goofy.common.UserProfileUrlProviderTestFactory.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; +import org.springframework.security.test.context.support.WithMockUser; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.command.CommandController; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserRole; +import de.itvsh.goofy.vorgang.Vorgang.VorgangStatus; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.VorgangWithEingang; +import de.itvsh.goofy.vorgang.VorgangWithEingangTestFactory; +import de.itvsh.goofy.vorgang.forwarding.ForwardingController; +import de.itvsh.goofy.vorgang.forwarding.ForwardingTestFactory; + +class VorgangWithEingangCommandProzessorTest { + + @Spy // NOSONAR + @InjectMocks + private VorgangWithEingangCommandProzessor processor; + @Mock + private CurrentUserService userService; + @Mock + private CommandController commandController; + @Mock + private ForwardingController forwardingController; + + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + private EntityModel<VorgangWithEingang> vorgangEntityModel = EntityModel.of(vorgang); + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + @Test + void shouldReturnEntityModel() { + initUserProfileUrlProvider(urlProvider); + + var result = processor.process(vorgangEntityModel); + + assertThat(result).isNotNull(); + } + + @Nested + class TestAddForwardLink { + + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.IN_BEARBEITUNG).build(); + + @BeforeEach + void init() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(true); + } + + @DisplayName("should be true for Role EINHEITLICHER_ANSPRECHPARTNER, Status IN_BEARBEITUNG and Forwarding with Error") + @Test + void shouldBeTrue() { + when(forwardingController.findFailedForwardings(anyString())).thenReturn(Collections.singletonList(ForwardingTestFactory.create())); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isTrue(); + } + + @Test + void shouldBeTrueInStatusNeu() { + var vorgang = VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.NEU).build(); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isTrue(); + } + + @Test + void shouldBeFalseForRoleNotEA() { + when(userService.hasRole(UserRole.EINHEITLICHER_ANSPRECHPARTNER)).thenReturn(false); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isFalse(); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "NEU", "IN_BEARBEITUNG" }) + void shouldBeFalseForVorgangInOtherStatus(VorgangStatus status) { + var vorgang = VorgangWithEingangTestFactory.createBuilder().status(status).build(); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isFalse(); + } + + @Test + void shouldBeFalseIfNoFailedForwarding() { + when(forwardingController.findFailedForwardings(anyString())).thenReturn(Collections.emptyList()); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isFalse(); + } + + @Test + void shouldBeFalseOnStatusWeitergeleitet() { + VorgangWithEingang vorgang = VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.WEITERGELEITET).build(); + + var addLink = processor.isForwardingAllowed(vorgang); + + assertThat(addLink).isFalse(); + } + + } + + @Nested + @DisplayName("add 'forward' link") + class TestForwardLink { + + private final LinkRelation linkRel = VorgangWithEingangCommandProzessor.REL_VORGANG_FORWARD; + + @BeforeEach + void init() { + initUserProfileUrlProvider(urlProvider); + } + + @Test + void shouldNotBePresent() { + doReturn(false).when(processor).isForwardingAllowed(any()); + + var processed = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())); + + assertThat(processed.getLink(linkRel)).isEmpty(); + } + + @Test + void shouldBePresent() { + doReturn(true).when(processor).isForwardingAllowed(any()); + + var processed = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())); + + assertThat(processed.getLink(linkRel)).isPresent().get() + .extracting(Link::getHref) + .isEqualTo("/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/relations/" + VorgangHeaderTestFactory.ID + "/" + + VorgangHeaderTestFactory.VERSION + "/commands"); + } + + } + + @Nested + class TestPendingCommandsLink { + + @BeforeEach + void init() { + initUserProfileUrlProvider(urlProvider); + } + + @Test + void shouldExists() { + when(commandController.existsPendingCommands(any())).thenReturn(true); + + var link = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())) + .getLink(VorgangWithEingangCommandProzessor.REL_PENDING_COMMANDS); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo("/api/commands?pending=true&vorgangId=" + VorgangHeaderTestFactory.ID); + } + + @Test + void shouldNotExists() { + when(commandController.existsPendingCommands(any())).thenReturn(false); + + var link = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())) + .getLink(VorgangWithEingangCommandProzessor.REL_PENDING_COMMANDS); + + assertThat(link).isNotPresent(); + } + + @DisplayName("should have link with user role " + UserRole.EINHEITLICHER_ANSPRECHPARTNER) + @WithMockUser(roles = UserRole.EINHEITLICHER_ANSPRECHPARTNER) + void shouldExistsByRoleEA() { + when(commandController.existsPendingCommands(any())).thenReturn(false); + + var link = processor.process(EntityModel.of(VorgangWithEingangTestFactory.create())) + .getLink(VorgangWithEingangCommandProzessor.REL_PENDING_COMMANDS); + + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo("/api/commands?pending=true&vorgangId=" + VorgangHeaderTestFactory.ID); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eefe10cdf97f3acefa57e233e420bccfc377aa03 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingControllerTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class ForwardingControllerTest { + + @InjectMocks + private ForwardingController controller; + @Mock + private ForwardingService service; + @Mock + private ForwardingModelAssembler modelAssembler; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestFindByVorgang { + @Test + void shouldReturn200() throws Exception { + doRequest().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).findByVorgang(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + doRequest(); + + verify(modelAssembler).toCollectionModel(Mockito.<Stream<Forwarding>>any()); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform(get(ForwardingController.LIST_PATH).param(ForwardingController.PARAM_VORGANG_ID, VorgangHeaderTestFactory.ID)); + } + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9e6057c84c9e515d2253a5c72879112f8a155ad2 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingLandesnetzInfoServiceTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.core.io.ResourceLoader; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.*; + +class ForwardingLandesnetzInfoServiceTest { + + @Spy + @InjectMocks + private ForwardingLandesnetzInfoService service; + @Mock + private ResourceLoader resourceLoader; + + @DisplayName("Is email in landesnetz") + @Nested + class TestIstInLandesnetz { + + private final String EMAIL_NAME = "testEmailName@"; + + private final String IN_LANDESNETZ = "itvsh.de"; + private final String IN_LANDESNETZ_WITH_DOTS = ".itvsh.old.de"; + + @BeforeEach + void initMap() throws Exception { + ReflectionTestUtils.setField(service, "landesnetzInfo", new HashSet<>(Arrays.asList(IN_LANDESNETZ, IN_LANDESNETZ_WITH_DOTS))); + } + + @DisplayName("validate with email in Landesnetz") + @Nested + class TestWithMatchingEmail { + + @Test + void shouldReturnTrueWithSingleDot() { + var result = service.isEmailInLandesnetz(EMAIL_NAME + IN_LANDESNETZ); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnTrueWithMultipleDots() { + var result = service.isEmailInLandesnetz(EMAIL_NAME + IN_LANDESNETZ_WITH_DOTS); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnTrueByConsideringJustTheEnd() { + var result = service.isEmailInLandesnetz(EMAIL_NAME + "me" + IN_LANDESNETZ_WITH_DOTS); + + assertThat(result).isTrue(); + } + } + + @DisplayName("validate with email NOT in Landesnetz") + @Nested + class TestEndsWithMatching { + + @Test + void shouldReturnFalseOnWrongDomain() { + var result = service.isEmailInLandesnetz(EMAIL_NAME + "me.gmx.de"); + + assertThat(result).isFalse(); + } + + @Test + void shouldReturnFalseOnConsideringJustTheEnd() { + var result = service.isEmailInLandesnetz(EMAIL_NAME + "me" + IN_LANDESNETZ_WITH_DOTS + ".nicht.gueltig.de"); + + assertThat(result).isFalse(); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e07d7cc132be135d9d8acc1a4ed63a4a4feb37bd --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingModelAssemblerTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; +import org.springframework.stereotype.Component; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.vorgang.forwarding.Forwarding.Status; + +@Component +class ForwardingModelAssemblerTest { + + @Spy // NOSONAR + @InjectMocks + private ForwardingModelAssembler modelAssembler; + + @Nested + class TestToModel { + + @Test + void shouldReturnEntityModel() { + var model = modelAssembler.toModel(ForwardingTestFactory.create()); + + assertThat(model).isNotNull(); + } + + @Test + void shouldHaveSelfLink() { + var model = modelAssembler.toModel(ForwardingTestFactory.create()); + + assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/forwardings/" + ForwardingTestFactory.ID); + } + } + + @Nested + class TestToCollectionModel { + @Test + void shouldCallToModel() { + modelAssembler.toCollectionModel(Stream.of(ForwardingTestFactory.create())); + + verify(modelAssembler).toModel(any()); + } + + @Test + void shouldHaveSelfLink() { + var model = modelAssembler.toCollectionModel(Stream.of(ForwardingTestFactory.create())); + + assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/forwardings"); + } + } + + @Nested + class TestLinkOnModel { + + private final String expectedLinkHref = "/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/relations/" + ForwardingTestFactory.ID + + "/-1/commands"; + + @Nested + class TestMarkAsSuccessLink { + + @Test + void shouldBePresentInStatusSENT() { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(Status.SENT).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_MARK_AS_SUCCESS)).isPresent().get() + .extracting(Link::getHref).isEqualTo(expectedLinkHref); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "SENT" }) + void shouldNOTBePresentInAnyOtherStatus(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_MARK_AS_SUCCESS)).isEmpty(); + } + } + + @Nested + class TestMarkAsFailLink { + + @ParameterizedTest + @EnumSource(names = { "SENT", "SUCCESSFULL" }) + void shouldBePresentInStatusSENT(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_MARK_AS_FAIL)).isPresent().get() + .extracting(Link::getHref).isEqualTo(expectedLinkHref); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "SENT", "SUCCESSFULL" }) + void shouldNOTBePresentInAnyOtherStatus(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_MARK_AS_FAIL)).isEmpty(); + } + } + + @Nested + class TestFailedLink { + + @Test + void shouldBePresentInStatusFAILED() { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(Status.FAILED).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_FAILED)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/forwardings/" + ForwardingTestFactory.ID); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "FAILED" }) + void shouldNOTBePresentInAnyOtherStatus(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_FAILED)).isEmpty(); + } + } + + @Nested + class TestSuccessfullLink { + + @Test + void shouldBePresentInStatusSUCCESSFULL() { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(Status.SUCCESSFULL).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_SUCCESSFULL)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/forwardings/" + ForwardingTestFactory.ID); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "SUCCESSFULL" }) + void shouldNOTBePresentInAnyOtherStatus(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_SUCCESSFULL)).isEmpty(); + } + } + + @Nested + class TestSendErrorLink { + + @Test + void shouldBePresentInStatusSEND_ERROR() { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(Status.SEND_ERROR).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_ERROR)).isPresent().get() + .extracting(Link::getHref).isEqualTo("/api/forwardings/" + ForwardingTestFactory.ID); + } + + @ParameterizedTest + @EnumSource(mode = Mode.EXCLUDE, names = { "SEND_ERROR" }) + void shouldNOTBePresentInAnyOtherStatus(Status status) { + var model = modelAssembler.toModel(ForwardingTestFactory.createBuilder().status(status).build()); + + assertThat(model.getLink(ForwardingModelAssembler.REL_ERROR)).isEmpty(); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidatorTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..83ee22aa9d566a7f4913aa5b8d1e382f4c766801 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingPasswordValidatorTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.vorgang.RedirectRequestTestFactory; + +class ForwardingPasswordValidatorTest { + + @Spy + @InjectMocks + private ForwardingPasswordValidator validator = new ForwardingPasswordValidator(); + @Mock + private ConstraintValidatorContext context; + @Mock + private ForwardingLandesnetzInfoService landesnetzInfoService; + + @DisplayName("Is valid") + @Nested + class TestIsValid { + + @DisplayName("with null/empty") + @Nested + class TestOnNullOrEmptyEmail { + + @Test + void shouldReturnTrue() { + var isValid = validator.isValid(RedirectRequestTestFactory.createBuilder().email(StringUtils.EMPTY).build(), context); + + assertThat(isValid).isTrue(); + } + } + + @DisplayName("for a doman in landesnetz") + @Nested + class TestOnIsInLandesnetz { + + @BeforeEach + void mock() { + when(landesnetzInfoService.isEmailInLandesnetz(anyString())).thenReturn(true); + } + + @Test + void shouldNotDoPasswordValidation() { + validator.isValid(RedirectRequestTestFactory.create(), context); + + verify(validator, never()).isValidPassword(any()); + } + } + + @DisplayName("for domain NOT in landesnetz") + @Nested + class TestOnIfNOTInLandesnetz { + + @Mock + private ConstraintViolationBuilder contraintBuilder; + + @BeforeEach + void mock() { + when(landesnetzInfoService.isEmailInLandesnetz(anyString())).thenReturn(false); + doNothing().when(validator).prepareConstraint(any()); + } + + @Test + void shouldDoPasswordValidation() { + validator.isValid(RedirectRequestTestFactory.create(), context); + + verify(validator).isValidPassword(any()); + } + } + + @DisplayName("password") + @Nested + class TestPasswordValidation { + @Test + void shouldReturnFalseOnLowerSize() { + var isValid = isValid("notEnou".toCharArray()); + + assertThat(isValid).isFalse(); + } + + @Test + void shouldReturnFalseOnLargerSize() { + var isValid = isValid(RandomStringUtils.randomAlphabetic(41).toCharArray()); + + assertThat(isValid).isFalse(); + } + + @Test + void shouldReturnFalseOnNull() { + var isValid = isValid(null); + + assertThat(isValid).isFalse(); + } + + @Test + void shouldReturnTrueOnValidSize() { + var isValid = isValid(RedirectRequestTestFactory.PASSWORD); + + assertThat(isValid).isTrue(); + } + + private boolean isValid(char[] password) { + return validator.isValidPassword(RedirectRequest.builder().password(password).build()); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ccc5cb9e94369ba7e22e31271c4e96ee650e9cab --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingRemoteServiceTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import de.itvsh.goofy.common.callcontext.ContextService; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.forwarding.ForwardingServiceGrpc.ForwardingServiceBlockingStub; +import de.itvsh.ozg.pluto.forwarding.GrpcFindForwardingsRequest; +import de.itvsh.ozg.pluto.forwarding.GrpcForwarding; +import de.itvsh.ozg.pluto.forwarding.GrpcForwardingsResponse; +import de.itvsh.ozg.pluto.grpc.command.GrpcCallContext; + +class ForwardingRemoteServiceTest { + + @InjectMocks // NOSONAR + private ForwardingRemoteService service; + + @Mock + private ForwardingServiceBlockingStub serviceStub; + @Mock + private ForwardingMapper mapper; + + @Mock + private ContextService contextService; + + @Captor + private ArgumentCaptor<GrpcFindForwardingsRequest> requestCaptor; + + private GrpcCallContext callContext = GrpcCallContext.newBuilder().build(); + + @BeforeEach + void initContextService() { + when(contextService.createCallContext()).thenReturn(callContext); + } + + @Nested + class TestFindForwardings { + private GrpcForwarding grpcForwarding = GrpcForwarding.newBuilder().build(); + private GrpcForwardingsResponse response = GrpcForwardingsResponse.newBuilder().addForwardings(grpcForwarding).build(); + + @BeforeEach + void initStub() { + when(serviceStub.findForwardings(any())).thenReturn(response); + } + + @Test + void shouldCallStub() { + service.findForwardings(VorgangHeaderTestFactory.ID); + + verify(serviceStub).findForwardings(any()); + } + + @Test + void shouldFillCallContext() { + service.findForwardings(VorgangHeaderTestFactory.ID); + + verify(serviceStub).findForwardings(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getContext()).isSameAs(callContext); + } + + @Test + void shouldHaveVorgangId() { + service.findForwardings(VorgangHeaderTestFactory.ID); + + verify(serviceStub).findForwardings(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallMapper() { + service.findForwardings(VorgangHeaderTestFactory.ID).forEach(v -> { + }); + + verify(mapper).fromGrpc(grpcForwarding); + } + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..58e30fc98f46a66918b1c586ebf6b46b57ed2889 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/ForwardingTestFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import java.util.UUID; + +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +public class ForwardingTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final String VORGANG_ID = VorgangHeaderTestFactory.ID; + + public static Forwarding create() { + return createBuilder().build(); + } + + public static Forwarding.ForwardingBuilder createBuilder() { + return Forwarding.builder() + .id(ID) + .vorgangId(VORGANG_ID); + + } + +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..359629b552a79d538152fbdb3d0a1fbc42943dc7 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/vorgang/forwarding/LandesnetzInfoReadServiceTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.vorgang.forwarding; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.kop.common.test.TestUtils; + +class LandesnetzInfoReadServiceTest { + + private LandesnetzInfoReadService service = new LandesnetzInfoReadService(); + + @Nested + class TestParseTable { + + private String HTML_VALUE; + + @BeforeEach + void initResource() { + HTML_VALUE = TestUtils.loadTextFile("files/LandesnetzInfo.html"); + } + + @Test + void shouldReadFileIfExist() { + var parsedDomainList = service.parseTableEntries(createDocument(HTML_VALUE)); + + assertThat(parsedDomainList).isNotNull().hasSize(6).containsExactlyInAnyOrder(".itvsh.de", "itvsh.de", ".itvsh.old.de", "itvsh.old.de", + "dataport.de", "ozg-sh.de"); + } + + @Test + void shouldReturnEmptyListIfFileNotExist() { + var parsedDomainList = service.parseTableEntries(createDocument("NOT_EXIST.html")); + + assertThat(parsedDomainList).isNotNull().isEmpty(); + } + + private Document createDocument(String source) { + return Jsoup.parse(source); + } + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/AttachmentsByWiedervorlageControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/AttachmentsByWiedervorlageControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5aa0ba47d600f2cc56ba80be77d2ef41a767a957 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/AttachmentsByWiedervorlageControllerTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.wiedervorlage.WiedervorlageController.AttachmentsByWiedervorlageController; + +class AttachmentsByWiedervorlageControllerTest { + + private final static String PATH = AttachmentsByWiedervorlageController.PATH.replace("{wiedervorlageId}", WiedervorlageTestFactory.ID) + + "/attachments"; + + @Spy + @InjectMocks + private AttachmentsByWiedervorlageController controller; + @Mock + private WiedervorlageService service; + @Mock + private BinaryFileController binaryFileController; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetAttachments { + + @BeforeEach + void mockService() { + when(service.getById(any())).thenReturn(WiedervorlageTestFactory.create()); + } + + @Test + void shouldReturnStatusOk() throws Exception { + callEndpoint().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).getById(WiedervorlageTestFactory.ID); + } + + @Test + void shouldCallBinaryFileController() throws Exception { + callEndpoint(); + + verify(binaryFileController).getFiles(List.of(BinaryFileTestFactory.FILE_ID)); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(PATH)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..83d2fd9b1e35c3d5045e9eac647b8ad9d53df00b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.command.CommandController.CommandByRelationController; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController; + +class WiedervorlageCommandByVorgangControllerTest { + + @Spy + @InjectMocks + private WiedervorlageCommandByVorgangController controller; + @Mock + private CommandByRelationController commandByRelationController; + @Mock + private CurrentUserService userService; + @Mock + private WiedervorlageService service; + + @Captor + private ArgumentCaptor<CreateCommand> createCommandCaptor; + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @DisplayName("Create command") + @Nested + class TestCreateCommand { + + @Captor + private ArgumentCaptor<Wiedervorlage> wiedervorlageCaptor; + + @BeforeEach + void mockUserService() { + when(service.createWiedervorlage(any(), any())).thenReturn(CommandTestFactory.create()); + } + + @Nested + class ServiceMethod { + + @Test + void shouldCallService() throws Exception { + doRequest(); + + verify(service).createWiedervorlage(wiedervorlageCaptor.capture(), eq(VorgangHeaderTestFactory.ID)); + assertThat(wiedervorlageCaptor.getValue().getAttachments().get(0)).isEqualTo(BinaryFileTestFactory.FILE_ID); + } + + @Test + void shouldCallServiceToUpdateNextFrist() throws Exception { + doRequest(); + + verify(service).updateNextFrist(VorgangHeaderTestFactory.ID); + } + + @Test + void returnReturnCreated() throws Exception { + doRequest().andExpect(status().isCreated()); + } + } + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform( + post(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG, + VorgangHeaderTestFactory.ID) + .content(WiedervorlageCommandTestFactory.createValidRequestContent()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00265de8617c99b82b7afb6ddf1d4ba14c9a3d0b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandControllerTest.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDate; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.FileId; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.command.CreateCommand; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.kop.common.errorhandling.TechnicalException; + +class WiedervorlageCommandControllerTest { + + @Spy + @InjectMocks + private WiedervorlageCommandController controller; + @Mock + private CommandService commandService; + @Mock + private WiedervorlageService service; + @Captor + private ArgumentCaptor<WiedervorlageCommand> commandCaptor; + @Captor + private ArgumentCaptor<CreateCommand> createCommandCaptor; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @DisplayName("Update wiedervorlage") + @Nested + class TestUpdateWiedervorlage { + + private static final String RESPONSE_HEADER = "http://localhost/api/commands/" + WiedervorlageCommandTestFactory.ID; + + @Nested + class ControllerMethods { + + @BeforeEach + void init() { + when(service.getById(any())).thenReturn(WiedervorlageTestFactory.create()); + when(service.editWiedervorlage(any(), any(), anyLong())).thenReturn(CommandTestFactory.createBuilder() + .order(CommandOrder.UPDATE_ATTACHED_ITEM) + .body(WiedervorlageTestFactory.createAsMap()).build()); + } + + @Test + void shouldCallServiceGetById() throws Exception { + doRequest(); + + verify(service).getById(WiedervorlageTestFactory.ID); + } + + @Test + void shouldCallServiceUpdateNextFrist() throws Exception { + doRequest(); + + verify(service).updateNextFrist(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldReturnCreatedStatus() throws Exception { + doRequest().andExpect(status().isCreated()); + } + + @Test + void shouldResponseWithLocationHeader() throws Exception { + doRequest().andExpect(header().string("Location", RESPONSE_HEADER)); + } + + @Test + void shouldCreateCommand() throws Exception { + doRequest(); + + verify(controller).createCommand(any(Wiedervorlage.class), any(WiedervorlageCommand.class)); + } + + private ResultActions doRequest() throws Exception { + return mockMvc.perform( + post(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS, WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION) + .content(WiedervorlageCommandTestFactory.createValidRequestContent()).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().is2xxSuccessful()); + } + } + + @DisplayName("create command") + @Nested + class TestCreateCommand { + + @DisplayName("for order 'erledigen'") + @Nested + class TestOnErledigenOrder { + + @Test + void shouldCallService() { + callCreateCommand(CommandOrder.WIEDERVORLAGE_ERLEDIGEN); + + verify(service).erledigen(any(Wiedervorlage.class)); + } + } + + @DisplayName("for order 'wiedereroeffnen'") + @Nested + class TestOnWiedereroeffnenOrder { + + @Test + void shouldCallService() { + callCreateCommand(CommandOrder.WIEDERVORLAGE_WIEDEREROEFFNEN); + + verify(service).wiedereroeffnen(any(Wiedervorlage.class)); + } + } + + @DisplayName("for order 'edit'") + @Nested + class TestOnEditOrder { + + @Test + void shouldCallService() { + callCreateCommand(CommandOrder.EDIT_WIEDERVORLAGE); + + verify(service).editWiedervorlage(any(Wiedervorlage.class), eq(WiedervorlageTestFactory.ID), + eq(WiedervorlageTestFactory.VERSION)); + } + + @DisplayName("update wiedervorlage by given command") + @Nested + class TestUpdateWiedervorlageByCommand { + + private final FileId ATTACHMENT_ID = FileId.from("73"); + private final String BETREFF = "Neuer Betreff"; + private final String BESCHREIBUNG = "Neuer Beschreibung"; + private final LocalDate FRIST = LocalDate.now(); + + @Test + void shouldHaveSetAttachments() { + var updatedWiedervorlage = update(); + + assertThat(updatedWiedervorlage.getAttachments()).hasSize(1); + assertThat(updatedWiedervorlage.getAttachments().get(0)).isEqualTo(ATTACHMENT_ID); + } + + @Test + void shouldHaveSetBetreff() { + var updatedWiedervorlage = update(); + + assertThat(updatedWiedervorlage.getBetreff()).isEqualTo(BETREFF); + } + + @Test + void shouldHaveSetBeschreibung() { + var updatedWiedervorlage = update(); + + assertThat(updatedWiedervorlage.getBeschreibung()).isEqualTo(BESCHREIBUNG); + } + + @Test + void shouldHaveSetFrist() { + var updatedWiedervorlage = update(); + + assertThat(updatedWiedervorlage.getFrist()).isEqualTo(FRIST); + } + + private Wiedervorlage update() { + return controller.updateWiedervorlageByCommand(WiedervorlageTestFactory.create(), buildWiedervorlageCommand()); + } + + private WiedervorlageCommand buildWiedervorlageCommand() { + var commandWiedervorlage = WiedervorlageTestFactory.createBuilder() + .clearAttachments().attachment(ATTACHMENT_ID) + .betreff(BETREFF) + .beschreibung(BESCHREIBUNG) + .frist(FRIST) + .build(); + return WiedervorlageCommandTestFactory.createBuilder().wiedervorlage(commandWiedervorlage).build(); + } + } + } + + @Nested + class OnUnsupportedOrder { + + @Test + void shouldThrowException() { + var wiedervorlage = WiedervorlageTestFactory.create(); + var command = WiedervorlageCommandTestFactory.createBuilder().order(CommandOrder.CREATE_ATTACHED_ITEM).build(); + + assertThatExceptionOfType(TechnicalException.class) + .isThrownBy(() -> controller.createCommand(wiedervorlage, command)); + } + } + + private Command callCreateCommand(CommandOrder order) { + return controller.createCommand(WiedervorlageTestFactory.create(), + WiedervorlageCommandTestFactory.createBuilder().order(order).build()); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..ac16b37821ec9d63a0ddfed102ed8bb119ec391b --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandITCase.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static de.itvsh.goofy.wiedervorlage.WiedervorlageCommandTestFactory.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDate; + +import org.assertj.core.internal.bytebuddy.utility.RandomString; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.ValidationMessageCodes; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandRemoteService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.goofy.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController; + +@AutoConfigureMockMvc +@SpringBootTest +@WithMockUser +class WiedervorlageCommandITCase { + + @Autowired + private MockMvc mockMvc; + @MockBean + private CommandRemoteService commandRemoteService; + @MockBean + private WiedervorlageRemoteService wiedervorlageRemoteService; + + @DisplayName("Create edit command") + @WithMockUser + @Nested + class TestCreateEditCommand { + + @BeforeEach + void initTest() { + when(wiedervorlageRemoteService.getById(anyString())).thenReturn(WiedervorlageTestFactory.create()); + when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldCreateCommand() throws Exception { + doRequest(createValidRequestContent()).andExpect(status().isCreated()); + + verify(commandRemoteService).createCommand(any()); + } + + @WithMockUser + @DisplayName("should return validation error") + @Nested + class TestValidation { + + @DisplayName("for null Betreff") + @Test + void createCommandWithInvalidBetreff() throws Exception { + String content = buildContentWithBetreff(null); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.betreff")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @DisplayName("for only 1 character in Betreff") + @Test + void createcommandWithShortBetreff() throws Exception { + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.betreff")); + + } + + @DisplayName("for invalid betreff should have paramter") + @Test + void minMaxParameter() throws Exception { + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()).andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); + } + + @DisplayName("for 41 character in Betreff") + @Test + void createCommandWithLongBetreff() throws Exception { + var content = buildContentWithBetreff(RandomString.make(41)); + + doRequest(content).andExpect(status().isUnprocessableEntity()); + } + + private String buildContentWithBetreff(String betreff) { + return createEditContent(WiedervorlageTestFactory.createBuilder().betreff(betreff).build()); + } + + @DisplayName("for frist in past") + @Test + void createCommandWithInvalidDate() throws Exception { + String content = createEditContent(WiedervorlageTestFactory.createBuilder().frist(LocalDate.parse("2020-01-01")).build()); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.frist")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_DATE_PAST)); + } + + @DisplayName("for empty frist") + @Test + void createCommandWithoutDate() throws Exception { + String content = createEditContent(WiedervorlageTestFactory.createBuilder().frist(null).build()); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.frist")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + private String createEditContent(Wiedervorlage wiedervorlage) { + return createRequestContent(createWithWiedervorlage(wiedervorlage).toBuilder().order(CommandOrder.EDIT_WIEDERVORLAGE).build()); + } + + } + + private ResultActions doRequest(String content) throws Exception { + return mockMvc.perform( + post(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS, WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); + } + } + + @DisplayName("Create create command") + @WithMockUser + @Nested + class TestCreateCreateCommand { + + private static final long RELATION_ID_ON_CREATE = -1; + + @BeforeEach + void initTest() { + when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); + when(wiedervorlageRemoteService.getById(anyString())).thenReturn(WiedervorlageTestFactory.create()); + } + + @Test + void shouldCreateCommand() throws Exception { + doRequest(createValidRequestContent()).andExpect(status().isCreated()); + + verify(commandRemoteService).createCommand(any()); + } + + @WithMockUser + @DisplayName("should return validation error") + @Nested + class TestValidation { + + @DisplayName("for null Betreff") + @Test + void createCommandWithInvalidBetreff() throws Exception { + String content = buildContentWithBetreff(null); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.betreff")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + @DisplayName("for only 1 character in Betreff") + @Test + void createcommandWithShortBetreff() throws Exception { + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.betreff")); + + } + + @DisplayName("for invalid betreff should have paramter") + @Test + void minMaxParameter() throws Exception { + String content = buildContentWithBetreff("a"); + + doRequest(content).andExpect(status().isUnprocessableEntity()).andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); + } + + @DisplayName("for 41 character in Betreff") + @Test + void createCommandWithLongBetreff() throws Exception { + var content = buildContentWithBetreff(RandomString.make(41)); + + doRequest(content).andExpect(status().isUnprocessableEntity()); + } + + private String buildContentWithBetreff(String betreff) { + return createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff(betreff).build())); + } + + @DisplayName("for frist in past") + @Test + void createCommandWithInvalidDate() throws Exception { + String content = createRequestContent( + createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().frist(LocalDate.parse("2020-01-01")).build())); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.frist")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_DATE_PAST)); + } + + @DisplayName("for empty frist") + @Test + void createCommandWithoutDate() throws Exception { + String content = createRequestContent( + createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().frist(null).build())); + + doRequest(content).andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.issues.length()").value(1)) + .andExpect(jsonPath("$.issues.[0].field").value("wiedervorlage.frist")) + .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); + } + + } + + private ResultActions doRequest(String content) throws Exception { + return mockMvc.perform(post(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, + RELATION_ID_ON_CREATE) + .contentType(MediaType.APPLICATION_JSON) + .content(content)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..d541b42b0103dd2883f328597b67096e006468c9 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageCommandTestFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import java.util.Objects; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.command.CommandOrder; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.kop.common.test.TestUtils; + +public class WiedervorlageCommandTestFactory { + + public static final String ID = CommandTestFactory.ID; + public static final CommandOrder ORDER = CommandOrder.EDIT_WIEDERVORLAGE; + + public static WiedervorlageCommand create() { + return createBuilder().build(); + } + + public static WiedervorlageCommand createWithWiedervorlage(Wiedervorlage wiedervorlage) { + return createBuilder().wiedervorlage(wiedervorlage).build(); + } + + public static WiedervorlageCommand.WiedervorlageCommandBuilder createBuilder() { + return WiedervorlageCommand.builder() + .id(ID) + .order(ORDER) + .wiedervorlage(WiedervorlageTestFactory.create()); + } + + public static String createValidRequestContent() { + return createRequestContent(create()); + } + + public static String createRequestContent(WiedervorlageCommand command) { + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl", + command.getOrder().name(), + addTuedelchen(command.getWiedervorlage().getBetreff()), + command.getWiedervorlage().getBeschreibung(), + Optional.ofNullable(command.getWiedervorlage().getFrist()).map(Object::toString).orElse(StringUtils.EMPTY), + "/api/binaryFiles/" + BinaryFileTestFactory.FILE_ID); + } + + private static String addTuedelchen(String str) { + return Objects.isNull(str) ? null : String.format("\"%s\"", str); + } + + public static String createRequestContent(CommandOrder order) { + return TestUtils.loadTextFile("jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl", order.name()); + } +} diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerITCase.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..47c171995394c3471843df41f480a7df73684209 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerITCase.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import de.itvsh.goofy.common.user.UserRole; + +@WithMockUser +@AutoConfigureMockMvc +@SpringBootTest +public class WiedervorlageControllerITCase { + + @MockBean + private WiedervorlageService service; + @Autowired + private MockMvc mockMvc; + + @WithMockUser + @Nested + class TestGetById { + + @WithMockUser(roles = { UserRole.EINHEITLICHER_ANSPRECHPARTNER }) + @Nested + class OnRoleEinheitlicherAnsprechpartner { + + @Test + void shouldReturnForbidden() throws Exception { + callEndpoint().andExpect(status().isForbidden()); + } + } + + @WithMockUser(roles = UserRole.VERWALTUNG_POSTSTELLE) + @Nested + class OnRoleVerwaltungPoststelle { + + @Test + void shouldReturnForbidden() throws Exception { + callEndpoint().andExpect(status().isForbidden()); + } + } + + @WithMockUser(roles = UserRole.VERWALTUNG_USER) + @Nested + class OnRoleVerwaltungUser { + + @BeforeEach + void mockController() { + when(service.getById(any())).thenReturn(WiedervorlageTestFactory.create()); + } + + @Test + void shouldReturnStatusOk() throws Exception { + callEndpoint().andExpect(status().isOk()); + } + + @Test + void shouldReturnResult() throws Exception { + callEndpoint().andExpect(jsonPath("$._links.self").exists()) + .andExpect(jsonPath("$._links.createdBy").exists()) + .andExpect(jsonPath("$.createdAt").value(WiedervorlageTestFactory.CREATED_AT_STR)) + .andExpect(jsonPath("$.frist").value(WiedervorlageTestFactory.FRIST_STR)); + } + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(WiedervorlageController.WIEDERVORLAGE_PATH + "/" + WiedervorlageTestFactory.ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7f1a801f4e28b8c0b43d203e2f98d39598a76be7 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageControllerTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.itvsh.goofy.common.binaryfile.BinaryFileController; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class WiedervorlageControllerTest { + + private final String PATH = WiedervorlageController.WIEDERVORLAGE_PATH + "/"; + + @Spy + @InjectMocks + private WiedervorlageController controller; + @Mock + private WiedervorlageService service; + @Mock + private WiedervorlageModelAssembler modelAssembler; + @Mock + private BinaryFileController binaryFileController; + @Mock + private WiedervorlageMapper mapper; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Nested + class TestGetById { + + private Wiedervorlage wiedervorlage = WiedervorlageTestFactory.create(); + + @BeforeEach + void mockService() { + when(service.getById(any())).thenReturn(wiedervorlage); + } + + @Test + void shouldReturnStatusOk() throws Exception { + callEndpoint().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).getById(WiedervorlageTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toModel(wiedervorlage); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(PATH + WiedervorlageTestFactory.ID)); + } + } + + @Nested + class TestFindByVorgang { + + private Wiedervorlage wiedervorlage = WiedervorlageTestFactory.create(); + + @BeforeEach + void mockService() { + when(service.findByVorgangId(any())).thenReturn(Stream.of(wiedervorlage)); + } + + @Test + void shouldReturnStatusOk() throws Exception { + callEndpoint().andExpect(status().isOk()); + } + + @Test + void shouldCallService() throws Exception { + callEndpoint(); + + verify(service).findByVorgangId(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallModelAssembler() throws Exception { + callEndpoint(); + + verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<Wiedervorlage>>any(), eq(VorgangHeaderTestFactory.ID)); + } + + private ResultActions callEndpoint() throws Exception { + return mockMvc.perform(get(PATH).param(WiedervorlageController.PARAM_VORGANG_ID, VorgangHeaderTestFactory.ID)); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapperTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a755d9f95a6bf3761f30cfb313cc64ed62cc5c85 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageMapperTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.common.binaryfile.FileIdMapper; +import de.itvsh.goofy.vorgang.GrpcVorgangAttachedItemTestFactory; +import de.itvsh.kop.pluto.common.grpc.GrpcObjectMapper; + +class WiedervorlageMapperTest { + + @Spy + @InjectMocks + private WiedervorlageMapper mapper = Mappers.getMapper(WiedervorlageMapper.class); + @Spy + private FileIdMapper fileIdMapper = Mappers.getMapper(FileIdMapper.class); + @Mock + private GrpcObjectMapper grpcObjectMapper; + + @DisplayName("Map from item") + @Nested + class TestFromItem { + + @BeforeEach + void mockMapper() { + when(grpcObjectMapper.mapFromGrpc(any())).thenReturn(WiedervorlageTestFactory.createAsMap()); + } + + @Test + void shouldCallGrpcObjectMapper() { + mapper.fromItem(GrpcVorgangAttachedItemTestFactory.create()); + + verify(grpcObjectMapper).mapFromGrpc(GrpcVorgangAttachedItemTestFactory.ITEM); + } + + @Test + void shouldCallMapperFromItemMap() { + mapper.fromItem(GrpcVorgangAttachedItemTestFactory.create()); + + verify(mapper).fromItemMap(WiedervorlageTestFactory.createAsMap(), GrpcVorgangAttachedItemTestFactory.VERSION); + } + } + + @DisplayName("Map from item map") + @Nested + class TestFromItemMap { + + @Test + void shouldMapId() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getId()).isEqualTo(WiedervorlageTestFactory.ID); + } + + @Test + void shouldMapVersion() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getVersion()).isEqualTo(WiedervorlageTestFactory.VERSION); + } + + @Test + void shouldMapDone() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.isDone()).isEqualTo(WiedervorlageTestFactory.DONE); + } + + @Test + void shouldMapCreatedBy() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getCreatedBy()).isEqualTo(WiedervorlageTestFactory.CREATED_BY); + } + + @Test + void shouldMapCreatedAt() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getCreatedAt()).isEqualTo(WiedervorlageTestFactory.CREATED_AT); + } + + @Test + void shouldMapBetreff() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getBetreff()).isEqualTo(WiedervorlageTestFactory.BETREFF); + } + + @Test + void shouldMapBeschreibung() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getBeschreibung()).isEqualTo(WiedervorlageTestFactory.BESCHREIBUNG); + } + + @Test + void shouldMapFrist() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getFrist()).isEqualTo(WiedervorlageTestFactory.FRIST); + } + + @Nested + class Attachments { + + @Test + void shouldReturnEmptyListOnNull() { + var wiedervorlageWithoutAttachments = new HashMap<String, Object>(); + wiedervorlageWithoutAttachments.putAll(WiedervorlageTestFactory.createAsMap()); + wiedervorlageWithoutAttachments.remove(WiedervorlageMapper.ATTACHMENTS); + + var wiedervorlage = mapper.fromItemMap(wiedervorlageWithoutAttachments, WiedervorlageTestFactory.VERSION); + + assertThat(wiedervorlage.getAttachments()).isEmpty(); + } + + @Test + void shouldMapSingleAttachment() { + var wiedervorlage = map(); + + assertThat(wiedervorlage.getAttachments()).contains(BinaryFileTestFactory.FILE_ID); + } + + @Test + void shouldMapAttachmentList() { + var wiedervorlage = mapper.fromItemMap(createWiedervorlageAsMapWithMultipleAttachments(), WiedervorlageTestFactory.VERSION); + + assertThat(wiedervorlage.getAttachments()).hasSize(2); + assertThat(wiedervorlage.getAttachments().get(0)).isEqualTo(BinaryFileTestFactory.FILE_ID); + assertThat(wiedervorlage.getAttachments().get(0)).isEqualTo(BinaryFileTestFactory.FILE_ID); + } + + private HashMap<String, Object> createWiedervorlageAsMapWithMultipleAttachments() { + var wiedervorlageItem = new HashMap<String, Object>(); + wiedervorlageItem.putAll(WiedervorlageTestFactory.createAsMap()); + wiedervorlageItem.put(WiedervorlageMapper.ATTACHMENTS, List.of(BinaryFileTestFactory.ID, BinaryFileTestFactory.ID)); + return wiedervorlageItem; + } + } + + private Wiedervorlage map() { + return mapper.fromItemMap(WiedervorlageTestFactory.createAsMap(), WiedervorlageTestFactory.VERSION); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssemblerTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a7100860aae4eb760342af8dcd08c6b78fd7ace --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageModelAssemblerTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.server.EntityLinks; + +import de.itvsh.goofy.common.UserProfileUrlProvider; +import de.itvsh.goofy.common.file.OzgFileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class WiedervorlageModelAssemblerTest { + + private final String PATH = WiedervorlageController.WIEDERVORLAGE_PATH + "/"; + + @InjectMocks + private WiedervorlageModelAssembler modelAssembler; + + @Mock + private EntityLinks entityLinks; + + private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + + @BeforeEach + void initTest() { + var context = mock(ApplicationContext.class); + var environment = mock(Environment.class); + when(environment.getProperty(anyString())).thenReturn("test/"); + when(context.getEnvironment()).thenReturn(environment); + urlProvider.setApplicationContext(context); + } + + @Nested + class TestLinksOnModel { + private static final String COMMAND_BY_WIEDERVORLAGE_PATH = WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS + .replace("{wiedervorlageId}", WiedervorlageTestFactory.ID) + .replace("{wiedervorlageVersion}", String.valueOf(WiedervorlageTestFactory.VERSION)); + + @Test + void shouldHaveSelfLink() { + var link = toModel().getLink(IanaLinkRelations.SELF); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(PATH + WiedervorlageTestFactory.ID); + } + + @Test + void shouldHaveEditLink() { + var link = toModel().getLink(WiedervorlageModelAssembler.REL_EDIT); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(COMMAND_BY_WIEDERVORLAGE_PATH); + } + + @Nested + class TestErledigenLink { + + @Test + void shouldHaveLink() { + var link = toModel().getLink(WiedervorlageModelAssembler.REL_ERLEDIGEN); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(COMMAND_BY_WIEDERVORLAGE_PATH); + } + + @Test + void shouldNotHaveLink() { + EntityModel<Wiedervorlage> entityModel = modelAssembler.toModel(WiedervorlageTestFactory.createBuilder().done(true).build()); + + var link = entityModel.getLink(WiedervorlageModelAssembler.REL_ERLEDIGEN); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class TestWiedereroeffnenLink { + @Test + void shouldHaveLink() { + EntityModel<Wiedervorlage> entityModel = modelAssembler.toModel(WiedervorlageTestFactory.createBuilder().done(true).build()); + + var link = entityModel.getLink(WiedervorlageModelAssembler.REL_WIEDEREROEFFNEN); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo(COMMAND_BY_WIEDERVORLAGE_PATH); + } + + @Test + void shouldNotHaveLink() { + EntityModel<Wiedervorlage> entityModel = modelAssembler.toModel(WiedervorlageTestFactory.create()); + + var link = entityModel.getLink(WiedervorlageModelAssembler.REL_WIEDEREROEFFNEN); + + assertThat(link).isNotPresent(); + } + } + + @Nested + class TestAttachmentsLink { + + @Test + void shouldHaveLink() { + EntityModel<Wiedervorlage> entityModel = modelAssembler + .toModel(WiedervorlageTestFactory.createBuilder().attachment(OzgFileTestFactory.ID).build()); + + var link = entityModel.getLink(WiedervorlageModelAssembler.REL_ATTACHMENTS); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo("/api/wiedervorlages/" + WiedervorlageTestFactory.ID + "/attachments"); + } + + @Test + void shouldNotHaveLink() { + EntityModel<Wiedervorlage> entityModel = modelAssembler + .toModel(WiedervorlageTestFactory.createBuilder().clearAttachments().build()); + + var link = entityModel.getLink(WiedervorlageModelAssembler.REL_ATTACHMENTS); + + assertThat(link).isNotPresent(); + } + } + + private EntityModel<Wiedervorlage> toModel() { + return modelAssembler.toModel(WiedervorlageTestFactory.create()); + } + } + + @Nested + class TestLinksOnCollectionModel { + + @Test + void shouldHaveCreateWiedervorlageLink() { + var collectionModel = modelAssembler.toCollectionModel(Collections.singleton(WiedervorlageTestFactory.create()).stream(), + VorgangHeaderTestFactory.ID); + + var link = collectionModel.getLink(WiedervorlageModelAssembler.REL_CREATE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo("/api/vorgangs/" + VorgangHeaderTestFactory.ID + "/wiedervorlageCommands"); + } + + @Test + void shouldHaveUploadFileLink() { + var collectionModel = modelAssembler.toCollectionModel(Collections.singleton(WiedervorlageTestFactory.create()).stream(), + VorgangHeaderTestFactory.ID); + + var link = collectionModel.getLink(WiedervorlageModelAssembler.REL_UPLOAD_FILE); + + assertThat(link).isPresent(); + assertThat(link.get().getHref()).isEqualTo("/api/binaryFiles/" + VorgangHeaderTestFactory.ID + "/wiedervorlageAttachment/file"); + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e8ff6cfe61fb0e7a7069a957fd38fdccd3df783a --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageRemoteServiceTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.context.ApplicationContext; + +import static org.assertj.core.api.Assertions.*; + +import de.itvsh.goofy.ApplicationTestFactory; +import de.itvsh.goofy.vorgang.GrpcVorgangAttachedItemTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; +import de.itvsh.ozg.pluto.grpc.clientAttribute.ClientAttributeServiceGrpc.ClientAttributeServiceBlockingStub; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcAccessPermission; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcDeleteClientAttributeRequest; +import de.itvsh.ozg.pluto.grpc.clientAttribute.GrpcSetClientAttributeRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcFindVorgangAttachedItemResponse; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemRequest; +import de.itvsh.ozg.pluto.vorgangAttachedItem.GrpcVorgangAttachedItemResponse; +import de.itvsh.ozg.pluto.vorgangAttachedItem.VorgangAttachedItemServiceGrpc.VorgangAttachedItemServiceBlockingStub; + +class WiedervorlageRemoteServiceTest { + + @Spy // NOSONAR + @InjectMocks + private WiedervorlageRemoteService service; + + @Mock + private ClientAttributeServiceBlockingStub clientAttributeServiceStub; + @Mock + private VorgangAttachedItemServiceBlockingStub vorgangAttachedItemServiceStub; + @Mock + private WiedervorlageMapper mapper; + @Mock + private ApplicationContext applicationContext; + + @Nested + class TestFindByVorgangId { + + private final GrpcFindVorgangAttachedItemResponse response = GrpcFindVorgangAttachedItemResponse.newBuilder() + .addVorgangAttachedItems(GrpcVorgangAttachedItemTestFactory.create()) + .build(); + + @Nested + class ServiceMethod { + + @BeforeEach + void initMocks() { + when(vorgangAttachedItemServiceStub.find(any())).thenReturn(response); + } + + @Test + void shouldCallVorgangAttachedItemStub() { + service.findByVorgangId(VorgangHeaderTestFactory.ID); + + verify(vorgangAttachedItemServiceStub).find(any(GrpcFindVorgangAttachedItemRequest.class)); + } + + @Nested + class Mapper { + + @BeforeEach + void mockGrpcMapper() { + when(mapper.fromItem(any())).thenReturn(WiedervorlageTestFactory.create()); + } + + @Test + void shouldCallForWiedervorlage() { + callFindById(); + + verify(mapper).fromItem(GrpcVorgangAttachedItemTestFactory.create()); + } + + private void callFindById() { + var result = service.findByVorgangId(VorgangHeaderTestFactory.ID); + collectStreamElementsToTriggerLazyStream(result); + } + + private void collectStreamElementsToTriggerLazyStream(Stream<Wiedervorlage> stream) { + stream.collect(Collectors.toList()); + } + } + } + + @Nested + class BuildRequest { + + @Test + void shouldSetVorgangId() { + var request = buildRequest(); + + assertThat(request.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldSetItemName() { + var request = buildRequest(); + + assertThat(request.getItemName()).isEqualTo(WiedervorlageRemoteService.ITEM_NAME); + } + + private GrpcFindVorgangAttachedItemRequest buildRequest() { + return service.buildFindRequest(VorgangHeaderTestFactory.ID); + } + } + } + + @Nested + class TestGetById { + + private final GrpcVorgangAttachedItemRequest request = GrpcVorgangAttachedItemRequest.newBuilder() + .setId(WiedervorlageTestFactory.ID).build(); + private final GrpcVorgangAttachedItemResponse response = GrpcVorgangAttachedItemResponse.newBuilder() + .setVorgangAttachedItem(GrpcVorgangAttachedItemTestFactory.create()) + .build(); + + @BeforeEach + void initMocks() { + when(vorgangAttachedItemServiceStub.getById(any())).thenReturn(response); + when(mapper.fromItem(any())).thenReturn(WiedervorlageTestFactory.create()); + } + + @Test + void shouldCallStub() { + service.getById(WiedervorlageTestFactory.ID); + + verify(vorgangAttachedItemServiceStub).getById(request); + } + + @Test + void shouldCallWiedervorlageMapper() { + service.getById(WiedervorlageTestFactory.ID); + + verify(mapper).fromItem(GrpcVorgangAttachedItemTestFactory.create()); + } + + @Test + void shouldSetVorgangId() { + var wiedervorlage = service.getById(WiedervorlageTestFactory.ID); + + assertThat(wiedervorlage.getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + } + + @Nested + class TestUpdateNextFrist { + + private static final LocalDate NEXT_FRIST = LocalDate.now(); + + @Test + void shouldSetOnExistingFrist() { + service.updateNextFrist(VorgangHeaderTestFactory.ID, Optional.of(NEXT_FRIST)); + + verify(service).setNextFrist(VorgangHeaderTestFactory.ID, NEXT_FRIST); + } + + @Test + void shouldDeleteOnNull() { + doNothing().when(service).deleteNextFrist(any()); + + service.updateNextFrist(VorgangHeaderTestFactory.ID, Optional.empty()); + + verify(service).deleteNextFrist(VorgangHeaderTestFactory.ID); + } + + @Nested + class OnExisting { + + @Captor + private ArgumentCaptor<GrpcSetClientAttributeRequest> requestCaptor; + + @Test + void shouldCallSetServiceStub() { + setNextFrist(); + + verify(clientAttributeServiceStub).set(any(GrpcSetClientAttributeRequest.class)); + } + + @Test + void shouldSetAccessPermissionToReadOnly() { + setNextFrist(); + + verify(clientAttributeServiceStub).set(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getAttribute().getAccess()).isEqualTo(GrpcAccessPermission.READ_ONLY); + } + + @Test + void shouldSetAttributeName() { + setNextFrist(); + + verify(clientAttributeServiceStub).set(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getAttribute().getAttributeName()) + .isEqualTo(WiedervorlageRemoteService.CLIENT_ATTRIBUTE_NEXT_WIEDERVORLAGE_FRIST); + } + + @Test + void shouldSetAttributeValue() { + setNextFrist(); + + verify(clientAttributeServiceStub).set(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getAttribute().getValue().getStringValue()) + .isEqualTo(NEXT_FRIST.format(DateTimeFormatter.ISO_DATE)); + } + + private void setNextFrist() { + service.setNextFrist(VorgangHeaderTestFactory.ID, NEXT_FRIST); + } + } + + @Nested + class OnNonExisting { + + @Captor + private ArgumentCaptor<GrpcDeleteClientAttributeRequest> requestCaptor; + + @BeforeEach + void init() { + when(applicationContext.getId()).thenReturn(ApplicationTestFactory.CONTEXT_ID); + } + + @Test + void shouldCallDeleteOnServiceStub() { + deleteNextFrist(); + + verify(clientAttributeServiceStub).delete(any(GrpcDeleteClientAttributeRequest.class)); + } + + @Test + void shouldCallWithAttributeName() { + deleteNextFrist(); + + verify(clientAttributeServiceStub).delete(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getAttributeName()) + .isEqualTo(WiedervorlageRemoteService.CLIENT_ATTRIBUTE_NEXT_WIEDERVORLAGE_FRIST); + } + + @Test + void shouldCallWithVorgangId() { + deleteNextFrist(); + + verify(clientAttributeServiceStub).delete(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getVorgangId()).isEqualTo(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallWithClient() { + deleteNextFrist(); + + verify(clientAttributeServiceStub).delete(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getClientName()).isEqualTo(ApplicationTestFactory.CONTEXT_ID); + } + + private void deleteNextFrist() { + service.deleteNextFrist(VorgangHeaderTestFactory.ID); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageServiceTest.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dd8f9730232d4211ecfab2011de19d158ab36fda --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageServiceTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.itvsh.goofy.common.attacheditem.VorgangAttachedItemService; +import de.itvsh.goofy.common.command.Command; +import de.itvsh.goofy.common.command.CommandService; +import de.itvsh.goofy.common.command.CommandTestFactory; +import de.itvsh.goofy.common.user.CurrentUserService; +import de.itvsh.goofy.common.user.UserProfileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +class WiedervorlageServiceTest { + + @Spy + @InjectMocks + private WiedervorlageService service; + @Mock + private WiedervorlageRemoteService remoteService; + @Mock + private VorgangAttachedItemService vorgangAttachedItemService; + @Mock + private CommandService commandService; + @Mock + private CurrentUserService currentUserService; + + @DisplayName("Create wiedervorlage") + @Nested + class TestCreateWiedervorlage { + + @DisplayName("should") + @Nested + class TestCalls { + + @BeforeEach + void mockServices() { + doReturn(WiedervorlageTestFactory.create()).when(service).addCreated(any()); + when(vorgangAttachedItemService.createNewWiedervorlage(any(), any())).thenReturn(CommandTestFactory.create()); + } + + @DisplayName("add created") + @Test + void shouldAddCreated() { + callCreateWiedervorlage(); + + verify(service).addCreated(any(Wiedervorlage.class)); + } + + @DisplayName("call vorgangattacheditem service") + @Test + void shouldCallVorgangAttachedItemService() { + callCreateWiedervorlage(); + + verify(vorgangAttachedItemService).createNewWiedervorlage(any(Wiedervorlage.class), eq(VorgangHeaderTestFactory.ID)); + } + } + + @DisplayName("Add created") + @Nested + class TestAddCreated { + + @BeforeEach + void mockServices() { + when(currentUserService.getUserId()).thenReturn(UserProfileTestFactory.ID); + } + + @Test + void shouldSetCreatedAt() throws Exception { + var wiedervorlage = callAddCreated(); + + assertThat(wiedervorlage.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS)); + } + + @Test + void shouldSetCreatedBy() throws Exception { + var wiedervorlage = callAddCreated(); + + assertThat(wiedervorlage.getCreatedBy()).isEqualTo(UserProfileTestFactory.ID.toString()); + } + + private Wiedervorlage callAddCreated() { + return service.addCreated(WiedervorlageTestFactory.createBuilder().createdAt(null).createdBy(null).build()); + } + } + + private Command callCreateWiedervorlage() { + return service.createWiedervorlage(WiedervorlageTestFactory.create(), VorgangHeaderTestFactory.ID); + } + } + + @DisplayName("Edit wiedervorlage") + @Nested + class TestEditWiedervorlage { + + @Captor + private ArgumentCaptor<Wiedervorlage> wiedervorlageCaptor; + + @Test + void shouldCallVorgangAttachedItemService() { + callEditWiedervorlage(); + + verify(vorgangAttachedItemService).editWiedervorlage(any(Wiedervorlage.class), any(), anyLong()); + } + + private Command callEditWiedervorlage() { + return service.editWiedervorlage(WiedervorlageTestFactory.create(), WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION); + } + } + + @Nested + class TestGetById { + + @Test + void shouldCallRemoteService() { + service.getById(WiedervorlageTestFactory.ID); + + verify(remoteService).getById(WiedervorlageTestFactory.ID); + } + } + + @Nested + class TestFindByVorgangId { + + @Test + void shouldCallRemoteService() { + service.findByVorgangId(VorgangHeaderTestFactory.ID); + + verify(remoteService).findByVorgangId(VorgangHeaderTestFactory.ID); + } + } + + @Nested + class TestUpdateNextFrist { + + @Nested + class ServiceMethod { + + @BeforeEach + void mockService() { + when(remoteService.findByVorgangId(any())).thenReturn(Stream.of(WiedervorlageTestFactory.create())); + } + + @Test + void shoulDoCalculation() { + callUpdateNextFrist(); + + verify(service).calculateNextFrist(ArgumentMatchers.<Stream<Wiedervorlage>>any()); + } + + @Test + void shouldCallFindByVorgangId() { + callUpdateNextFrist(); + + verify(service).findByVorgangId(VorgangHeaderTestFactory.ID); + } + + @Test + void shouldCallRemoteService() { + doReturn(Optional.of(WiedervorlageTestFactory.FRIST)).when(service).calculateNextFrist(any()); + + callUpdateNextFrist(); + + verify(remoteService).updateNextFrist(VorgangHeaderTestFactory.ID, Optional.of(WiedervorlageTestFactory.FRIST)); + } + + private void callUpdateNextFrist() { + service.updateNextFrist(VorgangHeaderTestFactory.ID); + } + } + + @Nested + class Calculation { + + @Test + void shouldReturnNullOnAllDone() { + var wiedervorlage = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().plus(1, ChronoUnit.DAYS)).done(true).build(); + + var nextFrist = calculateNextFrist(Stream.of(wiedervorlage)); + + assertThat(nextFrist).isEmpty(); + } + + @Test + void shouldReturnEarliestFrist() { + var fristPast2Days = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().minus(2, ChronoUnit.DAYS)).done(false) + .build(); + var fristPast1Day = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().minus(1, ChronoUnit.DAYS)).done(false).build(); + var fristFuture1Day = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().plus(1, ChronoUnit.DAYS)).done(false) + .build(); + var fristFuture2Days = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().plus(2, ChronoUnit.DAYS)).done(false) + .build(); + + var nextFrist = calculateNextFrist(Stream.of(fristPast2Days, fristPast1Day, fristFuture1Day, fristFuture2Days)); + + assertThat(nextFrist).contains(LocalDate.now().minus(2, ChronoUnit.DAYS)); + } + + @Test + void shouldReturnFristIgnoringDone() { + var fristPast1DayNotDone = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().plus(1, ChronoUnit.DAYS)).done(false) + .build(); + var fristPast1DayDone = WiedervorlageTestFactory.createBuilder().frist(LocalDate.now().minus(1, ChronoUnit.DAYS)).done(true) + .build(); + + var nextFrist = calculateNextFrist(Stream.of(fristPast1DayNotDone, fristPast1DayDone)); + + assertThat(nextFrist).contains(LocalDate.now().plus(1, ChronoUnit.DAYS)); + } + + private Optional<LocalDate> calculateNextFrist(Stream<Wiedervorlage> wiedervorlagen) { + return service.calculateNextFrist(wiedervorlagen); + } + } + } +} \ No newline at end of file diff --git a/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageTestFactory.java b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e10fc170578d1f79c8bcf1e20af2f4eb36cf4944 --- /dev/null +++ b/goofy-server/src/test/java/de/itvsh/goofy/wiedervorlage/WiedervorlageTestFactory.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.itvsh.goofy.wiedervorlage; + +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.Map; +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.itvsh.goofy.common.binaryfile.BinaryFileTestFactory; +import de.itvsh.goofy.vorgang.VorgangHeaderTestFactory; + +public class WiedervorlageTestFactory { + + public static final String ID = UUID.randomUUID().toString(); + public static final long VERSION = 73; + public static final boolean DONE = false; + public static final String CREATED_BY = UUID.randomUUID().toString(); + public static final String CREATED_BY_NAME = LoremIpsum.getInstance().getName(); + + public static final String BETREFF = "Lorem Ipsum"; + public static final String BESCHREIBUNG = "Lorem Ipsum dolores est"; + public static final String FRIST_STR = "2121-01-04"; + public static final LocalDate FRIST = LocalDate.parse(FRIST_STR); + public static final String CREATED_AT_STR = "2021-01-10T10:30:00Z"; + public static final ZonedDateTime CREATED_AT = ZonedDateTime.parse(CREATED_AT_STR); + + public static Wiedervorlage create() { + return createBuilder().build(); + } + + public static Wiedervorlage.WiedervorlageBuilder createBuilder() { + return Wiedervorlage.builder() + .id(ID) + .version(VERSION) + .done(DONE) + .betreff(BETREFF) + .beschreibung(BESCHREIBUNG) + .createdBy(CREATED_BY) + .createdAt(CREATED_AT) + .frist(FRIST) + .vorgangId(VorgangHeaderTestFactory.ID) + .attachment(BinaryFileTestFactory.FILE_ID); + } + + public static Map<String, Object> createAsMap() { + return Map.of( + WiedervorlageMapper.ID, ID, + WiedervorlageMapper.DONE, String.valueOf(DONE), + WiedervorlageMapper.CREATED_BY, CREATED_BY, + WiedervorlageMapper.CREATED_BY_NAME, CREATED_BY_NAME, + WiedervorlageMapper.BETREFF, BETREFF, + WiedervorlageMapper.BESCHREIBUNG, BESCHREIBUNG, + WiedervorlageMapper.FRIST, FRIST_STR, + WiedervorlageMapper.CREATED_AT, CREATED_AT_STR, + WiedervorlageMapper.ATTACHMENTS, BinaryFileTestFactory.ID); + } +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/goofy-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000000000000000000000000000000000000..79b126e6cdb86bec1f4f08c205de8961bde1934a --- /dev/null +++ b/goofy-server/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.mockito.junit.jupiter.MockitoExtension \ No newline at end of file diff --git a/goofy-server/src/test/resources/application-itcase.yml b/goofy-server/src/test/resources/application-itcase.yml new file mode 100644 index 0000000000000000000000000000000000000000..9d197cc5d6fe6d4eed19ff1b6619c47ac3437ad2 --- /dev/null +++ b/goofy-server/src/test/resources/application-itcase.yml @@ -0,0 +1,13 @@ +logging: + level: + ROOT: ERROR + +kop: + user-manager: + url: https://localhost + internalurl: http://localhost:8080 + search-template: /api/userProfiles/?searchBy={searchBy} + profile-template: /api/userProfiles/%s + upload: + maxFileSize: + postfachNachrichtAttachment: 3MB diff --git a/goofy-server/src/test/resources/application.yml b/goofy-server/src/test/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..21d2eb21d9d2215ef3ee7cd635fde3ccf446e47e --- /dev/null +++ b/goofy-server/src/test/resources/application.yml @@ -0,0 +1,22 @@ +spring: + profiles: + active: + - itcase + - local + mvc: + pathmatch: + matching-strategy: ant-path-matcher + +kop: + auth: + token: + secret: quatsch + validity: 60000 + user-manager: + url: https://localhost + internalurl: http://localhost:8080 + search-template: /api/userProfiles/?searchBy={searchBy} + profile-template: /api/userProfiles/%s + +goofy: + production: false \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/commandAssignedToBody b/goofy-server/src/test/resources/jsonTemplates/command/commandAssignedToBody new file mode 100644 index 0000000000000000000000000000000000000000..4858e4f247bfbbc6de0a37a048982fb0551893c0 --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/commandAssignedToBody @@ -0,0 +1,3 @@ + { + "assignedTo": %s + } \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithBody.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithBody.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..41b89955e8ec974e571d5ff8a6586ed49ca4c8c3 --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithBody.json.tmpl @@ -0,0 +1,4 @@ +{ + "order": "%s", + "body": %s +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..96999290ccd3538e3f396d603fbbdaa665f22f39 --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl @@ -0,0 +1,6 @@ +{ + "order": "%s", + "kommentar": { + "text": %s + } +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithPostfachMail.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithPostfachMail.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..1481e0b8af4c3810e80c70855a01bab4351b59b8 --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithPostfachMail.json.tmpl @@ -0,0 +1,9 @@ +{ + "order": "%s", + "body": { + "replyOption": "%s", + "subject": %s, + "mailBody": %s, + "attachments": ["%s"] + } +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithRedirectRequest.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithRedirectRequest.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..0e1be8961d1f1bdc4010e188d7e4ebce2331d637 --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithRedirectRequest.json.tmpl @@ -0,0 +1,8 @@ +{ + "order": "%s", + "redirectRequest": { + "email": %s, + "password": %s + }, + "body": null +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..fa1665155401142608bf6e6f08b1502a358ae8ca --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl @@ -0,0 +1,9 @@ +{ + "order": "%s", + "wiedervorlage": { + "betreff": %s, + "beschreibung": "%s", + "frist": "%s", + "attachments": ["%s"] + } +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createVorgangCommand.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createVorgangCommand.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..deddf3fb6e23dd58606ad0c0bb0a7501449eb70b --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createVorgangCommand.json.tmpl @@ -0,0 +1,5 @@ +{ + "order": "%s", + "body": null + +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..64d27c8846dd5cd0654e7af502fded4231eb7b4e --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl @@ -0,0 +1,3 @@ +{ + "order": "%s" +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/command/patchStatus.json b/goofy-server/src/test/resources/jsonTemplates/command/patchStatus.json new file mode 100644 index 0000000000000000000000000000000000000000..20a5c9066e1becb0e595789f03b82da35a833aea --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/command/patchStatus.json @@ -0,0 +1,3 @@ +{ + "status": "%s" +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/downloadTokenRequest.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/downloadTokenRequest.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..b7889317e3109cb467a78aa6c8a43f56b2bf10cd --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/downloadTokenRequest.json.tmpl @@ -0,0 +1,3 @@ +{ + "fileId":"%s" +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/jsonTemplates/vorgangWithEingang.json.tmpl b/goofy-server/src/test/resources/jsonTemplates/vorgangWithEingang.json.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..f549216198f886c64ef591c958b19fa7031946ba --- /dev/null +++ b/goofy-server/src/test/resources/jsonTemplates/vorgangWithEingang.json.tmpl @@ -0,0 +1,9 @@ +{ + "name": "testName", + "status": "NEU", + "createdAt": "2012-04-23T18:25:43.511Z", + "aktenzeichen": "sadfjkhw7r3", + "nummer": "1234567788", + "hasNewPostfachNachricht": false, + "eingang": {} +} \ No newline at end of file diff --git a/goofy-server/src/test/resources/junit-platform.properties b/goofy-server/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000000000000000000000000000000000..1cebb76d5a58ac034b2627d12411d82d1e85821e --- /dev/null +++ b/goofy-server/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled = true \ No newline at end of file diff --git a/goofy-server/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/goofy-server/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000000000000000000000000000000000..ca6ee9cea8ec189a088d50559325d4e84ff8ad09 --- /dev/null +++ b/goofy-server/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..4cbb865834f6128b8ddc16fc9b1966af99e52653 --- /dev/null +++ b/lombok.config @@ -0,0 +1,6 @@ +lombok.log.fieldName=LOG +lombok.log.slf4j.flagUsage = ERROR +lombok.log.log4j.flagUsage = ERROR +lombok.data.flagUsage = ERROR +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9e15abca833cf95193520f228b0a6c5bf0f681fe --- /dev/null +++ b/pom.xml @@ -0,0 +1,131 @@ +<!-- + + Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den + Ministerpräsidenten des Landes Schleswig-Holstein + Staatskanzlei + Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + + Lizenziert unter der EUPL, Version 1.2 oder - sobald + diese von der Europäischen Kommission genehmigt wurden - + Folgeversionen der EUPL ("Lizenz"); + Sie dürfen dieses Werk ausschließlich gemäß + dieser Lizenz nutzen. + Eine Kopie der Lizenz finden Sie hier: + + https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + + Sofern nicht durch anwendbare Rechtsvorschriften + gefordert oder in schriftlicher Form vereinbart, wird + die unter der Lizenz verbreitete Software "so wie sie + ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + ausdrücklich oder stillschweigend - verbreitet. + Die sprachspezifischen Genehmigungen und Beschränkungen + unter der Lizenz sind dem Lizenztext zu entnehmen. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>de.itvsh.ozg</groupId> + <artifactId>goofy</artifactId> + <version>1.1.1</version> + <name>Goofy Parent</name> + <packaging>pom</packaging> + + <parent> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-parent</artifactId> + <version>1.3.0</version> + </parent> + + <modules> + <module>goofy-client</module> + <module>goofy-server</module> + </modules> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + + <kop.license.version>1.3.0-SNAPSHOT</kop.license.version> + <pluto.version>1.1.0</pluto.version> + <jsoup.version>1.15.1</jsoup.version> + <kop-common-pdf.version>1.3.0</kop-common-pdf.version> + </properties> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>com.mycila</groupId> + <artifactId>license-maven-plugin</artifactId> + <version>4.1</version> + <configuration> + <mapping> + <ts>SLASHSTAR_STYLE</ts> + </mapping> + <licenseSets> + <licenseSet> + <header>license/eupl_v1_2_de/header.txt</header> + <excludes> + <exclude>**/README</exclude> + <exclude>src/test/resources/**</exclude> + <exclude>src/main/resources/**</exclude> + <exclude>**/goofy-e2e/reports/videos/**</exclude> + <exclude>**/.angular/cache/**</exclude> + </excludes> + </licenseSet> + </licenseSets> + </configuration> + <dependencies> + <dependency> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-license</artifactId> + <version>${kop.license.version}</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </pluginManagement> + </build> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>de.itvsh.ozg.pluto</groupId> + <artifactId>pluto-interface</artifactId> + <version>${pluto.version}</version> + </dependency> + <dependency> + <groupId>de.itvsh.ozg.pluto</groupId> + <artifactId>pluto-utils</artifactId> + <version>${pluto.version}</version> + </dependency> + <dependency> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-pdf</artifactId> + <version>${kop-common-pdf.version}</version> + </dependency> + <dependency> + <groupId>org.jsoup</groupId> + <artifactId>jsoup</artifactId> + <version>${jsoup.version}</version> + </dependency> + </dependencies> + </dependencyManagement> + + <distributionManagement> + <repository> + <id>ozg-nexus</id> + <name>ozg-releases</name> + <url>https://nexus.ozg-sh.de/repository/ozg-releases/</url> + </repository> + <snapshotRepository> + <id>ozg-snapshots-nexus</id> + <name>ozg-snapshots</name> + <url>https://nexus.ozg-sh.de/repository/ozg-snapshots/</url> + </snapshotRepository> + </distributionManagement> +</project>