Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
order-management-backend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ascend
order-management-backend
Commits
68dc4600
Commit
68dc4600
authored
May 11, 2021
by
Alex Segers
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'AFP-91' into dev
parents
9cfefa7f
a320e4c5
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
578 additions
and
145 deletions
+578
-145
.DS_Store
.DS_Store
+0
-0
.gitignore
.gitignore
+3
-0
pom.xml
pom.xml
+7
-10
AccessToken.java
.../java/com/afp/ordermanagement/annotation/AccessToken.java
+11
-0
AccessTokenResolver.java
...m/afp/ordermanagement/annotation/AccessTokenResolver.java
+28
-0
AuthManagerController.java
...afp/ordermanagement/annotation/AuthManagerController.java
+11
-0
AuthManagerResponse.java
...m/afp/ordermanagement/annotation/AuthManagerResponse.java
+11
-0
ManagerPayload.java
...va/com/afp/ordermanagement/annotation/ManagerPayload.java
+13
-0
ManagerPayloadResolver.java
...fp/ordermanagement/annotation/ManagerPayloadResolver.java
+58
-0
AnnotationConfig.java
...java/com/afp/ordermanagement/config/AnnotationConfig.java
+33
-0
AuthWebFilter.java
...in/java/com/afp/ordermanagement/config/AuthWebFilter.java
+27
-0
BeanConfig.java
src/main/java/com/afp/ordermanagement/config/BeanConfig.java
+46
-0
ManagerController.java
...com/afp/ordermanagement/controller/ManagerController.java
+37
-14
BadAccessTokenException.java
...fp/ordermanagement/exception/BadAccessTokenException.java
+11
-0
ControllerExceptionAdvice.java
.../ordermanagement/exception/ControllerExceptionAdvice.java
+74
-0
ErrorResponse.java
...java/com/afp/ordermanagement/exception/ErrorResponse.java
+13
-0
InvalidEntityResponse.java
.../afp/ordermanagement/exception/InvalidEntityResponse.java
+34
-0
ResourceNotFoundException.java
.../ordermanagement/exception/ResourceNotFoundException.java
+15
-0
Manager.java
src/main/java/com/afp/ordermanagement/model/Manager.java
+7
-21
ManagerRepository.java
...com/afp/ordermanagement/repository/ManagerRepository.java
+5
-3
ManagerSeeder.java
...in/java/com/afp/ordermanagement/seeder/ManagerSeeder.java
+31
-14
ManagerService.java
.../java/com/afp/ordermanagement/service/ManagerService.java
+32
-0
ManagerTokenVerifier.java
...com/afp/ordermanagement/service/ManagerTokenVerifier.java
+71
-0
.keep
src/main/resources/.keep
+0
-0
.DS_Store
src/test/java/com/afp/ordermanagement/UNIT_TESTS/.DS_Store
+0
-0
ValidatableEntity.java
.../ordermanagement/UNIT_TESTS/helper/ValidatableEntity.java
+0
-49
ManagerTests.java
...om/afp/ordermanagement/UNIT_TESTS/model/ManagerTests.java
+0
-34
No files found.
.DS_Store
deleted
100644 → 0
View file @
9cfefa7f
File deleted
.gitignore
View file @
68dc4600
...
@@ -34,3 +34,6 @@ build/
...
@@ -34,3 +34,6 @@ build/
### VS Code ###
### VS Code ###
.vscode/
.vscode/
### Mac OS ###
.DS_Store
pom.xml
View file @
68dc4600
...
@@ -25,14 +25,6 @@
...
@@ -25,14 +25,6 @@
<groupId>
org.springframework.boot
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-webflux
</artifactId>
<artifactId>
spring-boot-starter-webflux
</artifactId>
</dependency>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka-dist -->
<dependency>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<version>
1.18.20
</version>
<scope>
provided
</scope>
</dependency>
<dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<artifactId>
spring-boot-starter-test
</artifactId>
...
@@ -47,7 +39,7 @@
...
@@ -47,7 +39,7 @@
<dependency>
<dependency>
<groupId>
org.projectlombok
</groupId>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<artifactId>
lombok
</artifactId>
<version>
1.18.
16
</version>
<version>
1.18.
20
</version>
<scope>
provided
</scope>
<scope>
provided
</scope>
</dependency>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
<!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
...
@@ -85,11 +77,16 @@
...
@@ -85,11 +77,16 @@
<artifactId>
velocity
</artifactId>
<artifactId>
velocity
</artifactId>
<version>
1.5
</version>
<version>
1.5
</version>
</dependency>
</dependency>
<dependency>
<dependency>
<groupId>
io.projectreactor.kafka
</groupId>
<groupId>
io.projectreactor.kafka
</groupId>
<artifactId>
reactor-kafka
</artifactId>
<artifactId>
reactor-kafka
</artifactId>
</dependency>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
<dependency>
<groupId>
com.google.api-client
</groupId>
<artifactId>
google-api-client
</artifactId>
<version>
1.31.1
</version>
</dependency>
</dependencies>
</dependencies>
<build>
<build>
<plugins>
<plugins>
...
...
src/main/java/com/afp/ordermanagement/annotation/AccessToken.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
@Target
(
ElementType
.
PARAMETER
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
AccessToken
{
}
src/main/java/com/afp/ordermanagement/annotation/AccessTokenResolver.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
com.afp.ordermanagement.service.ManagerTokenVerifier
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.core.MethodParameter
;
import
org.springframework.web.reactive.BindingContext
;
import
org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver
;
import
org.springframework.web.server.ServerWebExchange
;
import
reactor.core.publisher.Mono
;
public
class
AccessTokenResolver
implements
HandlerMethodArgumentResolver
{
@Autowired
ManagerTokenVerifier
managerTokenVerifier
;
@Override
public
boolean
supportsParameter
(
MethodParameter
methodParameter
)
{
boolean
hasClassAnnotation
=
methodParameter
.
getDeclaringClass
().
isAnnotationPresent
(
AuthManagerController
.
class
);
boolean
hasMethodAnnotation
=
methodParameter
.
hasMethodAnnotation
(
AuthManagerResponse
.
class
);
boolean
hasParameterAnnotation
=
methodParameter
.
hasParameterAnnotation
(
AccessToken
.
class
);
return
(
hasClassAnnotation
||
hasMethodAnnotation
)
&&
hasParameterAnnotation
;
}
@Override
public
Mono
<
Object
>
resolveArgument
(
MethodParameter
methodParameter
,
BindingContext
bindingContext
,
ServerWebExchange
serverWebExchange
)
{
String
accessToken
=
managerTokenVerifier
.
getTokenHeader
(
serverWebExchange
);
return
Mono
.
just
(
accessToken
);
}
}
src/main/java/com/afp/ordermanagement/annotation/AuthManagerController.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
AuthManagerController
{
}
src/main/java/com/afp/ordermanagement/annotation/AuthManagerResponse.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
@Target
(
ElementType
.
METHOD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
AuthManagerResponse
{
}
src/main/java/com/afp/ordermanagement/annotation/ManagerPayload.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
@Target
(
ElementType
.
PARAMETER
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
ManagerPayload
{
String
value
()
default
""
;
String
pluck
()
default
""
;
}
src/main/java/com/afp/ordermanagement/annotation/ManagerPayloadResolver.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
annotation
;
import
com.afp.ordermanagement.model.Manager
;
import
com.afp.ordermanagement.service.ManagerTokenVerifier
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.core.MethodParameter
;
import
org.springframework.web.reactive.BindingContext
;
import
org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver
;
import
org.springframework.web.server.ServerWebExchange
;
import
reactor.core.publisher.Mono
;
import
java.lang.reflect.Field
;
import
java.util.Arrays
;
import
java.util.List
;
public
class
ManagerPayloadResolver
implements
HandlerMethodArgumentResolver
{
@Autowired
ManagerTokenVerifier
managerTokenVerifier
;
@Override
public
boolean
supportsParameter
(
MethodParameter
methodParameter
)
{
boolean
hasClassAnnotation
=
methodParameter
.
getDeclaringClass
().
isAnnotationPresent
(
AuthManagerController
.
class
);
boolean
hasMethodAnnotation
=
methodParameter
.
hasMethodAnnotation
(
AuthManagerResponse
.
class
);
boolean
hasParameterAnnotation
=
methodParameter
.
hasParameterAnnotation
(
ManagerPayload
.
class
);
return
(
hasClassAnnotation
||
hasMethodAnnotation
)
&&
hasParameterAnnotation
;
}
@Override
public
Mono
<
Object
>
resolveArgument
(
MethodParameter
methodParameter
,
BindingContext
bindingContext
,
ServerWebExchange
serverWebExchange
)
{
String
accessToken
=
managerTokenVerifier
.
getTokenHeader
(
serverWebExchange
);
ManagerPayload
annotation
=
methodParameter
.
getParameterAnnotation
(
ManagerPayload
.
class
);
String
value
=
annotation
.
value
(),
pluck
=
annotation
.
pluck
(),
selectedField
;
List
<
Field
>
declaredFields
=
Arrays
.
asList
(
Manager
.
class
.
getDeclaredFields
());
Manager
manager
=
managerTokenVerifier
.
createManagerFromToken
(
accessToken
);
if
(
value
.
isEmpty
()
&&
pluck
.
isEmpty
())
{
return
Mono
.
just
(
manager
);
}
else
{
selectedField
=
value
.
isEmpty
()
?
pluck
:
value
;
if
(!
declaredFields
.
contains
(
selectedField
))
return
Mono
.
just
(
manager
);
try
{
Field
field
=
Manager
.
class
.
getDeclaredField
(
selectedField
);
field
.
setAccessible
(
true
);
return
Mono
.
just
(
field
.
get
(
manager
));
}
catch
(
Exception
ignore
)
{
return
Mono
.
just
(
manager
);
}
}
}
}
\ No newline at end of file
src/main/java/com/afp/ordermanagement/config/AnnotationConfig.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
config
;
import
com.afp.ordermanagement.annotation.AccessTokenResolver
;
import
com.afp.ordermanagement.annotation.ManagerPayloadResolver
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.web.reactive.config.EnableWebFlux
;
import
org.springframework.web.reactive.config.WebFluxConfigurer
;
import
org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer
;
@Configuration
//@ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the classpath
//@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
//@EnableWebFlux
public
class
AnnotationConfig
implements
WebFluxConfigurer
{
@Override
public
void
configureArgumentResolvers
(
ArgumentResolverConfigurer
configurer
)
{
configurer
.
addCustomResolver
(
accessTokenResolver
());
configurer
.
addCustomResolver
(
managerPayloadResolver
());
}
@Bean
public
AccessTokenResolver
accessTokenResolver
()
{
return
new
AccessTokenResolver
();
}
@Bean
public
ManagerPayloadResolver
managerPayloadResolver
()
{
return
new
ManagerPayloadResolver
();
}
}
src/main/java/com/afp/ordermanagement/config/AuthWebFilter.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
config
;
import
com.afp.ordermanagement.exception.BadAccessTokenException
;
import
com.afp.ordermanagement.service.ManagerTokenVerifier
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.server.ServerWebExchange
;
import
org.springframework.web.server.WebFilter
;
import
org.springframework.web.server.WebFilterChain
;
import
reactor.core.publisher.Mono
;
@Component
public
class
AuthWebFilter
implements
WebFilter
{
@Autowired
ManagerTokenVerifier
managerTokenVerifier
;
@Override
public
Mono
<
Void
>
filter
(
ServerWebExchange
serverWebExchange
,
WebFilterChain
webFilterChain
)
{
// String origin = serverWebExchange.getRequest().getHeaders().getOrigin();
// if (managerTokenVerifier.hasTokenHeader(serverWebExchange)) {
// String token = managerTokenVerifier.getTokenHeader(serverWebExchange);
// if (managerTokenVerifier.isTokenValid(token))
return
webFilterChain
.
filter
(
serverWebExchange
);
// }
// return Mono.error(new BadAccessTokenException());
}
}
src/main/java/com/afp/ordermanagement/config/BeanConfig.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
config
;
import
com.afp.ordermanagement.model.Manager
;
import
com.github.javafaker.Faker
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
java.lang.reflect.Field
;
import
java.util.Arrays
;
import
static
java
.
util
.
Objects
.
isNull
;
@Configuration
public
class
BeanConfig
{
@Bean
public
static
Faker
faker
()
{
return
new
Faker
();
}
@Bean
public
static
FieldCombiner
fieldCombiner
()
{
return
new
FieldCombiner
();
}
public
static
class
FieldCombiner
{
public
Object
combine
(
Object
target
,
Object
source
,
String
[]
omitFields
)
{
try
{
for
(
Field
field:
Manager
.
class
.
getDeclaredFields
())
{
String
fieldName
=
field
.
getName
();
field
.
setAccessible
(
true
);
Object
sourceValue
=
field
.
get
(
source
);
if
(!
isNull
(
sourceValue
)
&&
Arrays
.
binarySearch
(
omitFields
,
fieldName
)
<=
0
)
field
.
set
(
target
,
sourceValue
);
}
}
catch
(
Exception
ignore
)
{
}
return
target
;
}
public
Object
combine
(
Object
target
,
Object
source
)
{
return
this
.
combine
(
target
,
source
,
new
String
[
0
]);
}
}
}
src/main/java/com/afp/ordermanagement/controller/ManagerController.java
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
controller
;
package
com
.
afp
.
ordermanagement
.
controller
;
import
com.afp.ordermanagement.annotation.AuthManagerController
;
import
com.afp.ordermanagement.annotation.ManagerPayload
;
import
com.afp.ordermanagement.exception.ResourceNotFoundException
;
import
com.afp.ordermanagement.model.Manager
;
import
com.afp.ordermanagement.model.Manager
;
import
com.afp.ordermanagement.repository.ManagerRepository
;
import
com.afp.ordermanagement.service.ManagerService
;
import
io.swagger.v3.oas.annotations.parameters.RequestBody
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.web.bind.annotation.RestController
;
import
org.springframework.web.bind.annotation.*
;
import
reactor.core.publisher.Flux
;
import
reactor.core.publisher.Mono
;
import
javax.validation.Valid
;
@Validated
@RestController
@RestController
@RequestMapping
(
"/api"
)
@AuthManagerController
@RequestMapping
(
"/api/managers/"
)
public
class
ManagerController
{
public
class
ManagerController
{
@Autowired
@Autowired
Manager
Repository
managerRepository
;
Manager
Service
managerService
;
@
GetMapping
(
"/manager
"
)
@
PostMapping
(
"/auth
"
)
public
Flux
<
Manager
>
getAllManagers
(
)
{
public
ResponseEntity
<
Mono
<
Manager
>>
signUpOrLogInManager
(
@ManagerPayload
Manager
managerPayload
)
{
System
.
out
.
println
(
"here"
);
Mono
<
Manager
>
manager
=
managerService
.
getByEmail
(
managerPayload
.
getEmail
())
Flux
<
Manager
>
managerFlux
=
managerRepository
.
findAll
(
);
.
switchIfEmpty
(
managerService
.
create
(
managerPayload
)
);
return
managerFlux
;
return
ResponseEntity
.
ok
(
manager
)
;
}
}
@GetMapping
(
"/account"
)
public
ResponseEntity
<
Mono
<
Manager
>>
getManagerDetails
(
@ManagerPayload
Manager
managerPayload
)
{
Mono
<
Manager
>
existingManager
=
managerService
.
getByEmail
(
managerPayload
.
getEmail
())
.
switchIfEmpty
(
Mono
.
error
(
new
ResourceNotFoundException
()));
return
ResponseEntity
.
ok
(
existingManager
);
}
@PatchMapping
(
"/account"
)
public
ResponseEntity
<
Mono
<
Manager
>>
updateManagerDetails
(
@ManagerPayload
Manager
managerPayload
,
@Valid
@RequestBody
Manager
managerBody
)
{
return
ResponseEntity
.
ok
(
managerService
.
updateByEmail
(
managerPayload
.
getEmail
(),
managerBody
));
}
@DeleteMapping
(
"/account"
)
public
ResponseEntity
deleteManagerDetails
(
@ManagerPayload
Manager
managerPayload
)
{
managerService
.
getByEmail
(
managerPayload
.
getEmail
())
.
switchIfEmpty
(
Mono
.
error
(
new
ResourceNotFoundException
()))
.
flatMap
(
managerService:
:
delete
);
return
ResponseEntity
.
noContent
().
build
();
}
}
}
src/main/java/com/afp/ordermanagement/exception/BadAccessTokenException.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
exception
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.web.bind.annotation.ResponseStatus
;
@ResponseStatus
(
value
=
HttpStatus
.
UNAUTHORIZED
)
public
class
BadAccessTokenException
extends
RuntimeException
{
public
BadAccessTokenException
()
{
super
(
"Invalid access token"
);
}
}
src/main/java/com/afp/ordermanagement/exception/ControllerExceptionAdvice.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
exception
;
import
lombok.RequiredArgsConstructor
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.http.server.reactive.ServerHttpRequest
;
import
org.springframework.web.bind.annotation.ControllerAdvice
;
import
org.springframework.web.bind.annotation.ExceptionHandler
;
import
org.springframework.web.bind.support.WebExchangeBindException
;
import
org.springframework.web.server.ServerWebExchange
;
@ControllerAdvice
@RequiredArgsConstructor
public
class
ControllerExceptionAdvice
{
@ExceptionHandler
(
BadAccessTokenException
.
class
)
public
ResponseEntity
<
ErrorResponse
>
handleBadAccessTokenException
(
RuntimeException
exc
,
ServerWebExchange
exchange
)
{
final
HttpStatus
status
=
HttpStatus
.
UNAUTHORIZED
;
final
ServerHttpRequest
request
=
exchange
.
getRequest
();
return
new
ResponseEntity
<>(
new
ErrorResponse
(
status
.
value
(),
request
.
getPath
().
value
(),
status
.
getReasonPhrase
(),
exc
.
getMessage
()
),
status
);
}
@ExceptionHandler
(
ResourceNotFoundException
.
class
)
public
ResponseEntity
<
ErrorResponse
>
handleEntityNotFoundException
(
RuntimeException
exc
,
ServerWebExchange
exchange
)
{
final
HttpStatus
status
=
HttpStatus
.
NOT_FOUND
;
final
ServerHttpRequest
request
=
exchange
.
getRequest
();
return
new
ResponseEntity
<>(
new
ErrorResponse
(
status
.
value
(),
request
.
getPath
().
value
(),
status
.
getReasonPhrase
(),
exc
.
getMessage
()
),
status
);
}
@ExceptionHandler
(
RuntimeException
.
class
)
public
ResponseEntity
<
ErrorResponse
>
handleRuntimeException
(
RuntimeException
exc
,
ServerWebExchange
exchange
)
{
final
HttpStatus
status
=
HttpStatus
.
INTERNAL_SERVER_ERROR
;
final
ServerHttpRequest
request
=
exchange
.
getRequest
();
return
new
ResponseEntity
<>(
new
ErrorResponse
(
status
.
value
(),
request
.
getPath
().
value
(),
status
.
getReasonPhrase
(),
exc
.
getMessage
()
),
status
);
}
@ExceptionHandler
(
WebExchangeBindException
.
class
)
public
ResponseEntity
<
ErrorResponse
>
webExchangeBindException
(
WebExchangeBindException
exc
,
ServerWebExchange
exchange
)
{
final
HttpStatus
status
=
exc
.
getStatus
();
final
ServerHttpRequest
request
=
exchange
.
getRequest
();
if
(
InvalidEntityResponse
.
isEntityValid
(
exc
.
getTarget
()))
{
return
new
ResponseEntity
<>(
new
ErrorResponse
(
status
.
value
(),
request
.
getPath
().
value
(),
status
.
getReasonPhrase
(),
exc
.
getMessage
()
),
status
);
}
return
new
ResponseEntity
<>(
new
InvalidEntityResponse
(
status
.
value
(),
request
.
getPath
().
value
(),
status
.
getReasonPhrase
(),
"Validation failed"
,
exc
.
getTarget
()
),
status
);
}
}
src/main/java/com/afp/ordermanagement/exception/ErrorResponse.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
exception
;
import
lombok.Data
;
import
lombok.RequiredArgsConstructor
;
import
java.time.Instant
;
@Data
@RequiredArgsConstructor
public
class
ErrorResponse
{
public
final
Integer
status
;
public
final
String
path
,
error
,
message
;
public
String
timestamp
=
Instant
.
now
().
toString
();
}
src/main/java/com/afp/ordermanagement/exception/InvalidEntityResponse.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
exception
;
import
javax.validation.ConstraintViolation
;
import
javax.validation.Validation
;
import
javax.validation.Validator
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
public
class
InvalidEntityResponse
extends
ErrorResponse
{
public
List
<
Map
<
String
,
String
>>
fieldErrors
;
public
InvalidEntityResponse
(
Integer
status
,
String
path
,
String
type
,
String
message
,
Object
entity
)
{
super
(
status
,
path
,
type
,
message
);
this
.
fieldErrors
=
InvalidEntityResponse
.
parseViolations
(
entity
);
}
public
static
boolean
isEntityValid
(
Object
entity
)
{
return
validator
.
validate
(
entity
).
isEmpty
();
}
static
private
final
Validator
validator
=
Validation
.
buildDefaultValidatorFactory
().
getValidator
();
static
private
List
<
Map
<
String
,
String
>>
parseViolations
(
Object
entity
)
{
List
<
Map
<
String
,
String
>>
fieldErrors
=
new
ArrayList
<>();
for
(
ConstraintViolation
<
Object
>
cv
:
validator
.
validate
(
entity
))
{
Map
<
String
,
String
>
errorMap
=
new
HashMap
<
String
,
String
>()
{{
put
(
"field"
,
cv
.
getPropertyPath
().
toString
());
put
(
"message"
,
cv
.
getMessage
());
}};
fieldErrors
.
add
(
errorMap
);
}
return
fieldErrors
;
}
}
src/main/java/com/afp/ordermanagement/exception/ResourceNotFoundException.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
exception
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.web.bind.annotation.ResponseStatus
;
@ResponseStatus
(
value
=
HttpStatus
.
NOT_FOUND
)
public
class
ResourceNotFoundException
extends
RuntimeException
{
public
ResourceNotFoundException
()
{
super
(
"Resource not found"
);
}
public
ResourceNotFoundException
(
String
message
)
{
super
(
message
);
}
}
src/main/java/com/afp/ordermanagement/model/Manager.java
View file @
68dc4600
...
@@ -4,12 +4,10 @@ import lombok.Data;
...
@@ -4,12 +4,10 @@ import lombok.Data;
import
lombok.NoArgsConstructor
;
import
lombok.NoArgsConstructor
;
import
org.hibernate.validator.constraints.URL
;
import
org.hibernate.validator.constraints.URL
;
import
org.springframework.data.annotation.Id
;
import
org.springframework.data.annotation.Id
;
import
org.springframework.data.mongodb.core.index.Indexed
;
import
org.springframework.data.mongodb.core.mapping.Document
;
import
org.springframework.data.mongodb.core.mapping.Document
;
import
javax.validation.constraints.*
;
import
javax.validation.constraints.*
;
import
java.lang.reflect.Field
;
import
static
java
.
util
.
Objects
.
isNull
;
@Data
@Data
@NoArgsConstructor
@NoArgsConstructor
...
@@ -18,30 +16,18 @@ public class Manager {
...
@@ -18,30 +16,18 @@ public class Manager {
@Id
@Id
private
String
id
;
private
String
id
;
@Size
(
min
=
1
,
max
=
250
,
message
=
"'firstName' must be between 1 & 250 characters"
)
@Size
(
min
=
1
,
max
=
250
)
@NotNull
@NotNull
()
private
String
firstName
,
lastName
;
private
String
firstName
,
lastName
;
@Indexed
(
unique
=
true
)
@Email
@Email
private
String
email
;
private
String
email
;
@
NotNull
@
Size
(
min
=
21
,
max
=
21
)
@
Size
(
min
=
21
,
max
=
21
,
message
=
"'googleId' is invalid"
)
@
NotNull
(
)
private
String
googleId
;
private
String
googleId
;
@URL
(
message
=
"'imageUrl' must be a valid URL"
)
@URL
private
String
imageUrl
;
private
String
imageUrl
;
static
public
Manager
combine
(
Manager
target
,
Manager
source
)
{
try
{
for
(
Field
field:
Manager
.
class
.
getDeclaredFields
())
{
String
fieldName
=
field
.
getName
();
Object
sourceValue
=
field
.
get
(
source
);
if
(!
isNull
(
sourceValue
)
&&
!
fieldName
.
equals
(
"id"
))
field
.
set
(
target
,
sourceValue
);
}
}
catch
(
Exception
ignore
)
{
}
return
target
;
}
}
}
src/main/java/com/afp/ordermanagement/repository/ManagerRepository.java
View file @
68dc4600
...
@@ -2,8 +2,10 @@ package com.afp.ordermanagement.repository;
...
@@ -2,8 +2,10 @@ package com.afp.ordermanagement.repository;
import
com.afp.ordermanagement.model.Manager
;
import
com.afp.ordermanagement.model.Manager
;
import
org.springframework.data.mongodb.repository.ReactiveMongoRepository
;
import
org.springframework.data.mongodb.repository.ReactiveMongoRepository
;
//import org.springframework.stereotype.Repository;
import
org.springframework.stereotype.Repository
;
//
import
reactor.core.publisher.Mono
;
//@Repository
@Repository
public
interface
ManagerRepository
extends
ReactiveMongoRepository
<
Manager
,
String
>
{
public
interface
ManagerRepository
extends
ReactiveMongoRepository
<
Manager
,
String
>
{
Mono
<
Manager
>
findByEmail
(
String
email
);
}
}
src/main/java/com/afp/ordermanagement/seeder/ManagerSeeder.java
View file @
68dc4600
...
@@ -6,8 +6,11 @@ import com.github.javafaker.Faker;
...
@@ -6,8 +6,11 @@ import com.github.javafaker.Faker;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.event.ContextRefreshedEvent
;
import
org.springframework.context.event.ContextRefreshedEvent
;
import
org.springframework.context.event.EventListener
;
import
org.springframework.context.event.EventListener
;
import
org.springframework.core.env.Environment
;
import
org.springframework.core.env.Profiles
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Component
;
import
java.util.Arrays
;
import
java.util.stream.IntStream
;
import
java.util.stream.IntStream
;
@Component
@Component
...
@@ -15,29 +18,43 @@ public class ManagerSeeder {
...
@@ -15,29 +18,43 @@ public class ManagerSeeder {
@Autowired
@Autowired
ManagerRepository
managerRepository
;
ManagerRepository
managerRepository
;
static
private
final
Faker
FAKER
=
new
Faker
();
@Autowired
Faker
faker
;
@Autowired
Environment
env
;
// Verify that the current environment is NOT production or test
boolean
isEnvDevelopment
()
{
return
env
.
acceptsProfiles
(
Profiles
.
of
(
"default"
,
"dev"
,
"development"
,
"local"
));
}
static
private
final
int
SEED_COUNT
=
1
;
// Number of seed documents generated
static
private
final
int
SEED_COUNT
=
10
;
// Create manager seeds as soon as app is up and running
@EventListener
@EventListener
public
void
seedManager
(
ContextRefreshedEvent
event
)
{
public
void
seedManagers
(
ContextRefreshedEvent
event
)
{
managerRepository
// Only generate seeds if in development environment
.
deleteAll
()
if
(
isEnvDevelopment
())
{
.
subscribe
();
IntStream
.
range
(
0
,
SEED_COUNT
).
forEach
(
n
->
{
managerRepository
managerRepository
.
insert
(
generateManager
()
)
.
deleteAll
(
)
.
subscribe
();
.
subscribe
();
});
IntStream
.
range
(
0
,
SEED_COUNT
+
1
).
forEach
(
n
->
{
managerRepository
.
insert
(
generateManager
())
.
subscribe
();
});
}
}
}
private
Manager
generateManager
(){
private
Manager
generateManager
(){
Manager
manager
=
new
Manager
();
Manager
manager
=
new
Manager
();
manager
.
setFirstName
(
FAKER
.
name
().
firstName
());
manager
.
setFirstName
(
faker
.
name
().
firstName
());
manager
.
setLastName
(
FAKER
.
name
().
lastName
());
manager
.
setLastName
(
faker
.
name
().
lastName
());
manager
.
setEmail
(
FAKER
.
internet
().
emailAddress
());
manager
.
setEmail
(
faker
.
internet
().
emailAddress
());
manager
.
setGoogleId
(
FAKER
.
number
().
digits
(
21
));
manager
.
setGoogleId
(
faker
.
number
().
digits
(
21
));
manager
.
setImageUrl
(
FAKER
.
internet
().
url
()
);
manager
.
setImageUrl
(
"https://picsum.photos/200/200"
);
return
manager
;
return
manager
;
}
}
}
}
src/main/java/com/afp/ordermanagement/service/ManagerService.java
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
service
;
package
com
.
afp
.
ordermanagement
.
service
;
import
com.afp.ordermanagement.model.Manager
;
import
com.afp.ordermanagement.repository.ManagerRepository
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
reactor.core.publisher.Flux
;
import
reactor.core.publisher.Mono
;
import
static
com
.
afp
.
ordermanagement
.
config
.
BeanConfig
.
FieldCombiner
;
@Service
public
class
ManagerService
{
public
class
ManagerService
{
@Autowired
FieldCombiner
fieldCombiner
;
@Autowired
ManagerRepository
managerRepository
;
public
Mono
<
Manager
>
getByEmail
(
String
email
)
{
return
managerRepository
.
findByEmail
(
email
);
}
public
Mono
<
Manager
>
create
(
Manager
newManager
)
{
return
managerRepository
.
save
(
newManager
);
}
public
Mono
<
Manager
>
updateByEmail
(
String
email
,
Manager
managerUpdates
)
{
return
this
.
getByEmail
(
email
)
.
map
(
existingManager
->
(
Manager
)
fieldCombiner
.
combine
(
existingManager
,
managerUpdates
,
new
String
[]
{
"id"
,
"email"
}))
.
flatMap
(
managerRepository:
:
save
);
}
public
Mono
<
Void
>
delete
(
Manager
manager
)
{
return
managerRepository
.
delete
(
manager
);
}
}
}
src/main/java/com/afp/ordermanagement/service/ManagerTokenVerifier.java
0 → 100644
View file @
68dc4600
package
com
.
afp
.
ordermanagement
.
service
;
import
com.afp.ordermanagement.model.Manager
;
import
com.google.api.client.googleapis.auth.oauth2.GoogleIdToken
;
import
com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier
;
import
com.google.api.client.http.HttpTransport
;
import
com.google.api.client.http.javanet.NetHttpTransport
;
import
com.google.api.client.json.jackson2.JacksonFactory
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.server.ServerWebExchange
;
import
java.util.Collections
;
import
static
java
.
util
.
Objects
.
isNull
;
@Service
public
class
ManagerTokenVerifier
{
private
final
String
CLIENT_ID
=
"925243198137-hhe2e3ejlethf321hh7tbm7ontc19cpj.apps.googleusercontent.com"
;
private
final
JacksonFactory
jsonFactory
=
new
JacksonFactory
();
private
final
HttpTransport
transport
=
new
NetHttpTransport
();
private
final
GoogleIdTokenVerifier
verifier
=
new
GoogleIdTokenVerifier
.
Builder
(
transport
,
jsonFactory
)
.
setAudience
(
Collections
.
singletonList
(
CLIENT_ID
))
.
build
();
public
boolean
isTokenValid
(
String
idTokenString
)
{
try
{
verifier
.
verify
(
idTokenString
);
return
true
;
}
catch
(
Exception
ignore
)
{
}
return
false
;
}
private
GoogleIdToken
.
Payload
createPayloadFromToken
(
String
idTokenString
)
{
GoogleIdToken
idToken
=
null
;
try
{
idToken
=
verifier
.
verify
(
idTokenString
);
}
catch
(
Exception
ignore
)
{
}
assert
idToken
!=
null
;
return
idToken
.
getPayload
();
}
public
Manager
createManagerFromToken
(
String
idTokenString
)
{
GoogleIdToken
.
Payload
payload
=
this
.
createPayloadFromToken
(
idTokenString
);
Manager
newManager
=
new
Manager
();
newManager
.
setFirstName
((
String
)
payload
.
get
(
"given_name"
));
newManager
.
setLastName
((
String
)
payload
.
get
(
"family_name"
));
newManager
.
setEmail
(
payload
.
getEmail
());
newManager
.
setGoogleId
(
payload
.
getSubject
());
newManager
.
setImageUrl
((
String
)
payload
.
get
(
"picture"
));
return
newManager
;
}
public
boolean
hasTokenHeader
(
ServerWebExchange
serverWebExchange
)
{
HttpHeaders
headers
=
serverWebExchange
.
getRequest
()
.
getHeaders
();
String
accessToken
=
headers
.
getFirst
(
"Authorization"
);
return
!
isNull
(
accessToken
);
}
public
String
getTokenHeader
(
ServerWebExchange
serverWebExchange
)
{
HttpHeaders
headers
=
serverWebExchange
.
getRequest
()
.
getHeaders
();
String
accessToken
=
headers
.
getFirst
(
"Authorization"
)
.
replace
(
"Bearer "
,
""
);
return
accessToken
;
}
}
src/main/resources/.keep
0 → 100644
View file @
68dc4600
src/test/java/com/afp/ordermanagement/UNIT_TESTS/.DS_Store
deleted
100644 → 0
View file @
9cfefa7f
File deleted
src/test/java/com/afp/ordermanagement/UNIT_TESTS/helper/ValidatableEntity.java
deleted
100644 → 0
View file @
9cfefa7f
package
com
.
afp
.
ordermanagement
.
UNIT_TESTS
.
helper
;
import
com.github.javafaker.Faker
;
//import com.sun.tools.javac.util.List;
import
org.junit.jupiter.api.Assertions
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.*;
import
javax.validation.ConstraintViolation
;
import
javax.validation.Validation
;
import
javax.validation.Validator
;
import
java.lang.reflect.ParameterizedType
;
import
java.util.List
;
public
abstract
class
ValidatableEntity
<
E
>
{
private
final
Class
<
E
>
childClass
;
static
private
final
Faker
FAKER
=
new
Faker
();
@SuppressWarnings
(
"unchecked"
)
public
ValidatableEntity
()
{
this
.
childClass
=
(
(
Class
<
E
>)
(
(
ParameterizedType
)
this
.
getClass
()
.
getGenericSuperclass
()
)
.
getActualTypeArguments
()[
0
]
);
}
private
static
final
Validator
validator
=
Validation
.
buildDefaultValidatorFactory
().
getValidator
();
// assertFieldValidation(String fieldName, Object input, boolean isValidInput)
public
ConstraintViolation
<
E
>
createViolation
(
String
fieldName
,
Object
fieldValue
)
{
return
validator
.
validateValue
(
this
.
childClass
,
fieldName
,
fieldValue
)
.
stream
()
.
findAny
()
.
orElse
(
null
);
}
// assertFieldValidation(String fieldName, Object input, boolean isValidInput)
public
void
assertFieldValidation
(
String
fieldName
,
Object
input
,
boolean
expected
)
{
boolean
isValid
=
validator
.
validateValue
(
this
.
childClass
,
fieldName
,
input
).
isEmpty
();
String
v
=
expected
?
"valid"
:
"invalid"
;
String
errMsg
=
String
.
format
(
"Expected '%s' to be a %s value for field '%s'"
,
input
,
v
,
fieldName
);
if
(
isValid
!=
expected
)
fail
(
errMsg
);
}
}
src/test/java/com/afp/ordermanagement/UNIT_TESTS/model/ManagerTests.java
deleted
100644 → 0
View file @
9cfefa7f
package
com
.
afp
.
ordermanagement
.
UNIT_TESTS
.
model
;
import
com.afp.ordermanagement.UNIT_TESTS.helper.ValidatableEntity
;
import
com.afp.ordermanagement.model.Manager
;
import
org.junit.jupiter.params.ParameterizedTest
;
import
org.junit.jupiter.params.provider.CsvSource
;
public
class
ManagerTests
extends
ValidatableEntity
<
Manager
>
{
@ParameterizedTest
@CsvSource
(
value
=
{
"null:false"
,
"\"\":false"
,
"Jerry:true"
},
delimiter
=
':'
)
public
void
shouldValidateNameFields
(
String
input
,
boolean
expected
)
{
assertFieldValidation
(
"firstName"
,
input
,
expected
);
assertFieldValidation
(
"lastName"
,
input
,
expected
);
}
@ParameterizedTest
@CsvSource
(
value
=
{
"null:false"
,
"\"\":false"
,
"poop:false"
,
"jerry@hotmail.com:true"
},
delimiter
=
':'
)
public
void
shouldValidateEmailField
(
String
input
,
boolean
expected
)
{
assertFieldValidation
(
"email"
,
input
,
expected
);
}
@ParameterizedTest
@CsvSource
(
value
=
{
"null:false"
,
"\"\":false"
},
delimiter
=
':'
)
public
void
shouldValidateGoogleIdField
(
String
input
,
boolean
expected
)
{
assertFieldValidation
(
"googleId"
,
input
,
expected
);
}
@ParameterizedTest
@CsvSource
(
value
=
{
"\"\",false"
,
"https://picsum.photos/200,true"
,
"htps: picsum.photos/200,false"
})
public
void
shouldValidateImageUrl
(
String
input
,
boolean
expected
)
{
assertFieldValidation
(
"imageUrl"
,
input
,
expected
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment