Skip to content
This repository has been archived by the owner on Feb 8, 2025. It is now read-only.

Commit

Permalink
switching code to kotlin to protect against NPEs
Browse files Browse the repository at this point in the history
  • Loading branch information
hlafaille committed Dec 25, 2024
1 parent bd69168 commit 54606ee
Show file tree
Hide file tree
Showing 28 changed files with 405 additions and 484 deletions.
24 changes: 5 additions & 19 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,6 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -135,6 +129,11 @@
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>2.0.0</version> <!-- Use your Kotlin version -->
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
Expand Down Expand Up @@ -184,10 +183,6 @@
<source>21</source>
<target>21</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand All @@ -199,15 +194,6 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
package com.kerosenelabs.billtracker.argumentresolver;
package com.kerosenelabs.billtracker.argumentresolver

import java.util.UUID;

import com.kerosenelabs.billtracker.exception.AuthException;
import com.kerosenelabs.billtracker.model.OAuth2Provider;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.kerosenelabs.billtracker.entity.UserEntity;
import com.kerosenelabs.billtracker.service.UserService;
import com.kerosenelabs.billtracker.entity.UserEntity
import com.kerosenelabs.billtracker.exception.AuthException
import com.kerosenelabs.billtracker.service.UserService
import org.springframework.core.MethodParameter
import org.springframework.lang.Nullable
import org.springframework.stereotype.Component
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.method.support.ModelAndViewContainer
import java.util.*

@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
private UserService userService;

public UserArgumentResolver(UserService userService) {
this.userService = userService;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.getParameterType() != null && parameter.getParameterType().equals(UserEntity.class))
&& (parameter.getParameterName() != null && parameter.getParameterName().equals("user"));
class UserArgumentResolver(private val userService: UserService) : HandlerMethodArgumentResolver {
override fun supportsParameter(parameter: MethodParameter): Boolean {
return (parameter.parameterType == UserEntity::class.java)
&& (parameter.parameterName != null && parameter.parameterName == "user")
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
@Throws(Exception::class)
override fun resolveArgument(
parameter: MethodParameter,
@Nullable mavContainer: ModelAndViewContainer?,
webRequest: NativeWebRequest,
@Nullable binderFactory: WebDataBinderFactory?
): Any {
// parse auth jwt
String authHeader = webRequest.getHeader("Authorization");
if (authHeader == null) {
throw new AuthException("Authorization header is required");
}
authHeader = authHeader.replace("Bearer ", "");
String userId = userService.getIdFromJwt(authHeader);
return userService.getUserById(UUID.fromString(userId));
var authHeader = webRequest.getHeader("Authorization")
?: throw AuthException("Authorization header is required")
authHeader = authHeader.replace("Bearer ", "")
val userId = userService.getIdFromJwt(authHeader)
return userService.getUserById(UUID.fromString(userId))
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package com.kerosenelabs.billtracker.config;
package com.kerosenelabs.billtracker.config

import com.kerosenelabs.billtracker.model.response.ErrorResponse;
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 org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.kerosenelabs.billtracker.exception.AuthException;
import com.kerosenelabs.billtracker.exception.UnconfirmedUserException;
import com.kerosenelabs.billtracker.exception.AuthException
import com.kerosenelabs.billtracker.exception.UnconfirmedUserException
import com.kerosenelabs.billtracker.model.response.ErrorResponse
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AuthException.class)
public ResponseEntity<ErrorResponse> handleAuthException(AuthException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse(e.getMessage()));
class GlobalExceptionHandler {
@ExceptionHandler(AuthException::class)
fun handleAuthException(e: AuthException): ResponseEntity<ErrorResponse> {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ErrorResponse(e.message.toString()))
}

@ExceptionHandler(UnconfirmedUserException.class)
public ResponseEntity<ErrorResponse> handleUnconfirmedUserException(UnconfirmedUserException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(e.getMessage()));
@ExceptionHandler(UnconfirmedUserException::class)
fun handleUnconfirmedUserException(e: UnconfirmedUserException): ResponseEntity<ErrorResponse> {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ErrorResponse(e.message.toString()))
}
}

This file was deleted.

61 changes: 28 additions & 33 deletions src/main/java/com/kerosenelabs/billtracker/config/WebConfig.kt
Original file line number Diff line number Diff line change
@@ -1,45 +1,40 @@
package com.kerosenelabs.billtracker.config;
package com.kerosenelabs.billtracker.config

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.kerosenelabs.billtracker.argumentresolver.UserArgumentResolver;
import com.kerosenelabs.billtracker.argumentresolver.UserArgumentResolver
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.method.support.HandlerMethodArgumentResolver
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
public class WebConfig implements WebMvcConfigurer {
private UserArgumentResolver userArgumentResolver;

public WebConfig(UserArgumentResolver userArgumentResolver) {
this.userArgumentResolver = userArgumentResolver;
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userArgumentResolver);
open class WebConfig(private val userArgumentResolver: UserArgumentResolver) : WebMvcConfigurer {
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
resolvers.add(userArgumentResolver)
}

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
open fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173", "https://localhost", "https://dev.billtracker.kerosenelabs.com", "https://billtracker.kerosenelabs.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
.allowedOrigins(
"http://localhost:5173",
"https://localhost",
"https://dev.billtracker.kerosenelabs.com",
"https://billtracker.kerosenelabs.com"
)
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600)
}
};
}
}

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(org.springframework.http.MediaType.APPLICATION_JSON);
override fun configureContentNegotiation(configurer: ContentNegotiationConfigurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.kerosenelabs.billtracker.controller;
package com.kerosenelabs.billtracker.controller

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestController

@RestController
public class ExpensesController {
}
class ExpensesController
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
package com.kerosenelabs.billtracker.controller;
package com.kerosenelabs.billtracker.controller

import com.kerosenelabs.billtracker.entity.UserEntity;
import com.kerosenelabs.billtracker.exception.AuthException;
import com.kerosenelabs.billtracker.model.request.HandleTokenRequest;
import com.kerosenelabs.billtracker.model.response.HandleOAuth2TokenResponse;
import com.kerosenelabs.billtracker.service.GoogleOAuth2ProviderService;
import com.kerosenelabs.billtracker.service.UserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import com.kerosenelabs.billtracker.exception.AuthException
import com.kerosenelabs.billtracker.model.request.HandleTokenRequest
import com.kerosenelabs.billtracker.model.response.HandleOAuth2TokenResponse
import com.kerosenelabs.billtracker.service.GoogleOAuth2ProviderService
import com.kerosenelabs.billtracker.service.UserService
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import java.io.IOException

@RestController
@Tag(name = "OAuth2")
public class OAuth2Controller {
private final GoogleOAuth2ProviderService googleOAuth2ProviderService;
private final UserService userService;

public OAuth2Controller(GoogleOAuth2ProviderService googleOAuth2ProviderService, UserService userService) {
this.googleOAuth2ProviderService = googleOAuth2ProviderService;
this.userService = userService;
}

class OAuth2Controller(
private val googleOAuth2ProviderService: GoogleOAuth2ProviderService,
private val userService: UserService
) {
@PostMapping("/oauth/google")
@ResponseStatus(HttpStatus.OK)
public HandleOAuth2TokenResponse handleToken(@RequestBody HandleTokenRequest request) throws IOException, AuthException {
UserEntity user = googleOAuth2ProviderService.handleCode(request.getCode());
String jwt = userService.establishJwt(user);
return new HandleOAuth2TokenResponse(jwt);
@Throws(
IOException::class,
AuthException::class
)
fun handleToken(@RequestBody request: HandleTokenRequest): HandleOAuth2TokenResponse {
val user = googleOAuth2ProviderService.handleCode(request.code)
val jwt = userService.establishJwt(user)
return HandleOAuth2TokenResponse(jwt)
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
package com.kerosenelabs.billtracker.controller;
package com.kerosenelabs.billtracker.controller

import com.kerosenelabs.billtracker.entity.UserEntity;
import com.kerosenelabs.billtracker.model.request.SetIntroductorySettingsRequest;
import com.kerosenelabs.billtracker.model.response.GetIntroductorySettingsResponse;
import com.kerosenelabs.billtracker.service.SettingsService;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import com.kerosenelabs.billtracker.entity.UserEntity
import com.kerosenelabs.billtracker.model.request.SetIntroductorySettingsRequest
import com.kerosenelabs.billtracker.model.response.GetIntroductorySettingsResponse
import com.kerosenelabs.billtracker.service.SettingsService
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*

@RestController
@Tag(name = "Settings", description = "User settings")
public class SettingsController {
private final SettingsService settingsService;

public SettingsController(SettingsService settingsService) {
this.settingsService = settingsService;
}

@PutMapping(value = "/settings/introductions", consumes = "application/json")
class SettingsController(private val settingsService: SettingsService) {
@PutMapping(value = ["/settings/introductions"], consumes = ["application/json"])
@ResponseStatus(HttpStatus.NO_CONTENT)
public void setIntroductions(@Parameter(hidden = true) UserEntity user, @RequestBody SetIntroductorySettingsRequest request) {
settingsService.setIntroductorySettings(user, request.getFirstName(), request.getLastName(), request.getBirthday());
fun setIntroductions(
@Parameter(hidden = true) user: UserEntity,
@RequestBody request: SetIntroductorySettingsRequest
) {
settingsService.setIntroductorySettings(user, request.firstName, request.lastName, request.birthday)
}

@GetMapping("/settings/introductions")
@ResponseStatus(HttpStatus.OK)
public GetIntroductorySettingsResponse getIntroductions(@Parameter(hidden = true) UserEntity user) {
return new GetIntroductorySettingsResponse(settingsService.getIntroductorySettings(user));
fun getIntroductions(@Parameter(hidden = true) user: UserEntity): GetIntroductorySettingsResponse {
return GetIntroductorySettingsResponse(settingsService.getIntroductorySettings(user))
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package com.kerosenelabs.billtracker.controller;

import com.kerosenelabs.billtracker.service.UserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
package com.kerosenelabs.billtracker.controller

import com.kerosenelabs.billtracker.service.UserService
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.RestController

@RestController
@Tag(name = "User")
public class UserController {
private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}
}
class UserController(private val userService: UserService)
Loading

0 comments on commit 54606ee

Please sign in to comment.