Add Checkstyle and Prettier to BFF #65

Merged
stne3960 merged 15 commits from chore/checkstyle into main 2026-01-13 13:17:19 +01:00
18 changed files with 634 additions and 149 deletions

View File

@ -0,0 +1,21 @@
name: Code style BFF
on:
pull_request:
push:
branches:
- main
jobs:
check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: bff
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 25
cache: maven
- name: Run checkstyle and prettier
run: ./mvnw validate --batch-mode

1
bff/.gitignore vendored
View File

@ -1 +1,2 @@
target/ target/
node_modules

2
bff/.prettierrc.yaml Normal file
View File

@ -0,0 +1,2 @@
plugins:
- prettier-plugin-java

View File

@ -0,0 +1,203 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
- the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
- the Javadoc guidelines at
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
- the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
https://checkstyle.org (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
https://checkstyle.org/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/filefilters/index.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/filters/suppressionfilter.html -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Checks whether files end with a new line. -->
<!-- See https://checkstyle.org/checks/misc/newlineatendoffile.html -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See https://checkstyle.org/checks/misc/translation.html -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/checks/sizes/index.html -->
<module name="FileLength"/>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/checks/whitespace/index.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/checks/misc/index.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<module name="TreeWalker">
<!-- Checks for Javadoc comments. -->
<!-- See https://checkstyle.org/checks/javadoc/index.html -->
<module name="InvalidJavadocPosition"/>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>
<module name="JavadocType">
<property name="allowMissingParamTags" value="true"/>
</module>
<module name="JavadocStyle"/>
<!-- Checks for Naming Conventions. -->
<!-- See https://checkstyle.org/checks/naming/index.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName">
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
</module>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See https://checkstyle.org/checks/imports/index.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See https://checkstyle.org/checks/sizes/index.html -->
<module name="MethodLength"/>
<module name="ParameterNumber">
<property name="max" value="15"/>
</module>
<!-- Checks for whitespace -->
<!-- See https://checkstyle.org/checks/whitespace/index.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
</module>
<!-- Modifier Checks -->
<!-- See https://checkstyle.org/checks/modifier/index.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See https://checkstyle.org/checks/blocks/index.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly">
<property name="option" value="eol"/>
</module>
<module name="NeedBraces">
<property name="allowSingleLineStatement" value="true"/>
</module>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See https://checkstyle.org/checks/coding/index.html -->
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber"/>
<module name="MissingSwitchDefault"/>
<module name="MultipleVariableDeclarations"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See https://checkstyle.org/checks/design/index.html -->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier"/>
<!-- Miscellaneous other checks. -->
<!-- See https://checkstyle.org/checks/misc/index.html -->
<module name="ArrayTypeStyle"/>
<module name="TodoComment"/>
<module name="UpperEll"/>
<!-- https://checkstyle.org/filters/suppressionxpathfilter.html -->
<module name="SuppressionXpathFilter">
<property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
default="checkstyle-xpath-suppressions.xml" />
<property name="optional" value="true"/>
</module>
</module>
</module>

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
<suppressions>
<suppress files=".*[\\/]target[\\/]generated-sources[\\/].*" checks=".*"/>
<suppress checks="HideUtilityClassConstructor"
files="Studentportalen\.java"/>
</suppressions>

140
bff/package-lock.json generated Normal file
View File

@ -0,0 +1,140 @@
{
"name": "bff",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"prettier-plugin-java": "^2.8.1"
}
},
"node_modules/@chevrotain/cst-dts-gen": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
"integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@chevrotain/gast": "11.0.3",
"@chevrotain/types": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/@chevrotain/gast": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
"integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@chevrotain/types": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/@chevrotain/regexp-to-ast": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
"integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@chevrotain/types": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
"integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@chevrotain/utils": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
"integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/chevrotain": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@chevrotain/cst-dts-gen": "11.0.3",
"@chevrotain/gast": "11.0.3",
"@chevrotain/regexp-to-ast": "11.0.3",
"@chevrotain/types": "11.0.3",
"@chevrotain/utils": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/chevrotain-allstar": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz",
"integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash-es": "^4.17.21"
},
"peerDependencies": {
"chevrotain": "^11.0.0"
}
},
"node_modules/java-parser": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/java-parser/-/java-parser-3.0.1.tgz",
"integrity": "sha512-sDIR7u9b7O2JViNUxiZRhnRz7URII/eE7g2B+BmGxDeS6Ex3OYAcCyz5oh0H4LQ+hL/BS8OJTz8apMy9xtGmrQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"chevrotain": "11.0.3",
"chevrotain-allstar": "0.3.1",
"lodash": "4.17.21"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true,
"license": "MIT"
},
"node_modules/prettier": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-java": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-2.8.1.tgz",
"integrity": "sha512-tkteH5OSCEb0E7wKnhhUSitr1pGUCUt9M//CwerSNhoalL/qv0jXTeSVBPZ36KC+kZl3nbq4dxh144NuGchACg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"java-parser": "3.0.1"
},
"peerDependencies": {
"prettier": "^3.0.0"
}
}
}
}

5
bff/package.json Normal file
View File

@ -0,0 +1,5 @@
{
"devDependencies": {
"prettier-plugin-java": "^2.8.1"
}
}

View File

@ -20,6 +20,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>25</java.version> <java.version>25</java.version>
<springdoc.version>2.8.6</springdoc.version> <springdoc.version>2.8.6</springdoc.version>
<checkstyle.version>10.21.4</checkstyle.version>
</properties> </properties>
<dependencies> <dependencies>
@ -60,8 +61,92 @@
</annotationProcessorPaths> </annotationProcessorPaths>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>${checkstyle.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>checkstyle-configuration.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
</configuration>
</plugin>
<plugin>
<groupId>com.hubspot.maven.plugins</groupId>
<artifactId>prettier-maven-plugin</artifactId>
<version>0.22</version>
<configuration>
<prettierJavaVersion>2.8.1</prettierJavaVersion>
</configuration>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>2.0.0</version>
<configuration>
<nodeVersion>v22.13.0</nodeVersion>
<installDirectory>${project.build.directory}</installDirectory>
</configuration>
</plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>install-node-and-npm</id>
<phase>validate</phase>
<goals>
<goal>install-node-and-npm</goal>
</goals>
</execution>
<execution>
<id>npm-install</id>
<phase>validate</phase>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.hubspot.maven.plugins</groupId>
<artifactId>prettier-maven-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build> </build>
<profiles> <profiles>

View File

@ -3,5 +3,4 @@ package se.su.dsv.studentportalen.bff.config;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("se.su.dsv.backend-api") @ConfigurationProperties("se.su.dsv.backend-api")
public record BackendApiConfiguration(String daisyUrl) { public record BackendApiConfiguration(String daisyUrl) {}
}

View File

@ -6,7 +6,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public record FrontendConfiguration(String url) { public record FrontendConfiguration(String url) {
public FrontendConfiguration { public FrontendConfiguration {
if (url == null || url.isBlank()) { if (url == null || url.isBlank()) {
throw new IllegalArgumentException("se.su.dsv.frontend.url must not be null or blank"); throw new IllegalArgumentException(
"se.su.dsv.frontend.url must not be null or blank"
);
} }
} }
} }

View File

@ -1,5 +1,6 @@
package se.su.dsv.studentportalen.bff.config; package se.su.dsv.studentportalen.bff.config;
import java.util.List;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -7,34 +8,42 @@ import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint; import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint;
import java.util.List;
@Configuration @Configuration
public class SecurityConfiguration { public class SecurityConfiguration {
@Bean @Bean
public SecurityFilterChain securityFilterChain( public SecurityFilterChain securityFilterChain(
HttpSecurity http, HttpSecurity http,
FrontendConfiguration frontendConfiguration) FrontendConfiguration frontendConfiguration
throws Exception ) throws Exception {
{ http.exceptionHandling(exception ->
http.exceptionHandling(exception -> exception exception.authenticationEntryPoint(new BFFAuthenticationEntryPoint())
.authenticationEntryPoint(new BFFAuthenticationEntryPoint())); );
http.oauth2Login(login -> login http.oauth2Login(login ->
.defaultSuccessUrl(frontendConfiguration.url(), true)); login.defaultSuccessUrl(frontendConfiguration.url(), true)
http.authorizeHttpRequests(authorize -> authorize );
.requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**").permitAll() http.authorizeHttpRequests(authorize ->
.anyRequest().authenticated()); authorize
http.cors(cors -> cors .requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**")
.configurationSource(_ -> frontendOnlyCors(frontendConfiguration))); .permitAll()
.anyRequest()
.authenticated()
);
http.cors(cors ->
cors.configurationSource(_ -> frontendOnlyCors(frontendConfiguration))
);
http.csrf(csrf -> csrf.spa()); http.csrf(csrf -> csrf.spa());
return http.build(); return http.build();
} }
private static CorsConfiguration frontendOnlyCors(FrontendConfiguration frontendConfiguration) { private static CorsConfiguration frontendOnlyCors(
FrontendConfiguration frontendConfiguration
) {
var corsConfiguration = new CorsConfiguration(); var corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url())); corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url()));
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); corsConfiguration.setAllowedMethods(
List.of("GET", "POST", "PUT", "DELETE")
);
// Allow the frontend to see the X-Authorization-Url header // Allow the frontend to see the X-Authorization-Url header
corsConfiguration.setExposedHeaders(List.of("X-Authorization-Url")); corsConfiguration.setExposedHeaders(List.of("X-Authorization-Url"));
@ -45,7 +54,9 @@ public class SecurityConfiguration {
// Content-Type is allowed by default but with a restriction on the value // Content-Type is allowed by default but with a restriction on the value
// The restriction does not allow "application/json" so we add it as an allowed header // The restriction does not allow "application/json" so we add it as an allowed header
// X-XSRF-TOKEN is needed for CSRF protection // X-XSRF-TOKEN is needed for CSRF protection
corsConfiguration.setAllowedHeaders(List.of("Content-Type", "X-XSRF-TOKEN")); corsConfiguration.setAllowedHeaders(
List.of("Content-Type", "X-XSRF-TOKEN")
);
return corsConfiguration; return corsConfiguration;
} }
} }

View File

@ -24,8 +24,8 @@ public class ProfileController {
@GetMapping("/profile") @GetMapping("/profile")
public ProfileResponse getProfile( public ProfileResponse getProfile(
@AuthenticationPrincipal(errorOnInvalidType = true) OAuth2User currentUser) @AuthenticationPrincipal(errorOnInvalidType = true) OAuth2User currentUser
{ ) {
return service.getProfile(currentUser); return service.getProfile(currentUser);
} }
} }

View File

@ -1,15 +1,14 @@
package se.su.dsv.studentportalen.bff.controller; package se.su.dsv.studentportalen.bff.controller;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import se.su.dsv.studentportalen.bff.config.BackendApiConfiguration;
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope; import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Subtask; import java.util.concurrent.StructuredTaskScope.Subtask;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import se.su.dsv.studentportalen.bff.config.BackendApiConfiguration;
@RestController @RestController
@RequestMapping("/test") @RequestMapping("/test")
@ -34,7 +33,8 @@ public class TestController {
// Pick the scope based on the desired behaviour // Pick the scope based on the desired behaviour
try (var scope = StructuredTaskScope.open()) { try (var scope = StructuredTaskScope.open()) {
Subtask<String> nameTask = scope.fork(() -> { Subtask<String> nameTask = scope.fork(() -> {
String name = restClient.get() String name = restClient
.get()
.uri("/test/name") .uri("/test/name")
.retrieve() .retrieve()
.body(String.class); .body(String.class);
@ -42,7 +42,8 @@ public class TestController {
}); });
Subtask<String> emailTask = scope.fork(() -> { Subtask<String> emailTask = scope.fork(() -> {
String email = restClient.get() String email = restClient
.get()
.uri("/test/email") .uri("/test/email")
.retrieve() .retrieve()
.body(String.class); .body(String.class);
@ -50,10 +51,11 @@ public class TestController {
}); });
Subtask<DaisyProfile> daisyProfile = scope.fork(() -> { Subtask<DaisyProfile> daisyProfile = scope.fork(() -> {
DaisyProfile profile = restClient.get() DaisyProfile profile = restClient
.uri(backendApiConfiguration.daisyUrl(), builder -> builder .get()
.path("/profile") .uri(backendApiConfiguration.daisyUrl(), builder ->
.build()) builder.path("/profile").build()
)
.retrieve() .retrieve()
.body(DaisyProfile.class); .body(DaisyProfile.class);
return profile; return profile;
@ -65,7 +67,8 @@ public class TestController {
return "Hello, I am %s and my email is %s. My Daisy profile is: %s".formatted( return "Hello, I am %s and my email is %s. My Daisy profile is: %s".formatted(
nameTask.get(), nameTask.get(),
emailTask.get(), emailTask.get(),
daisyProfile.get()); daisyProfile.get()
);
} }
} }

View File

@ -1,22 +1,21 @@
package se.su.dsv.studentportalen.bff.dto.response; package se.su.dsv.studentportalen.bff.dto.response;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects; import java.util.Objects;
/** /**
* User profile information. * User profile information.
*/ */
public record ProfileResponse( public record ProfileResponse(
@JsonProperty(value = "name", required = true) @JsonProperty(value = "name", required = true) String name,
String name,
@JsonProperty(value = "language", required = true) @JsonProperty(value = "language", required = true) Language language
Language language) ) {
{
public enum Language { public enum Language {
@JsonProperty("sv") SWEDISH, @JsonProperty("sv")
@JsonProperty("en") ENGLISH SWEDISH,
@JsonProperty("en")
ENGLISH,
} }
public ProfileResponse { public ProfileResponse {

View File

@ -7,12 +7,13 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint { public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @Override
public void commence( public void commence(
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
AuthenticationException authException) AuthenticationException authException
{ ) {
String loginUri = ServletUriComponentsBuilder.fromRequest(request) String loginUri = ServletUriComponentsBuilder.fromRequest(request)
.replacePath("/oauth2/authorization/studentportalen") .replacePath("/oauth2/authorization/studentportalen")
.build() .build()

View File

@ -5,6 +5,10 @@ WORKDIR /build
COPY mvnw mvnw COPY mvnw mvnw
COPY .mvn/ .mvn/ COPY .mvn/ .mvn/
COPY pom.xml pom.xml COPY pom.xml pom.xml
COPY checkstyle-configuration.xml checkstyle-configuration.xml
COPY checkstyle-suppressions.xml checkstyle-suppressions.xml
COPY package.json package.json
COPY package-lock.json package-lock.json
RUN ./mvnw dependency:go-offline \ RUN ./mvnw dependency:go-offline \
--batch-mode \ --batch-mode \