Ajout du support pour les étapes des recettes dans l'API REST.
This commit is contained in:
parent
fe9dcacc7c
commit
854d3c2c3e
Binary file not shown.
|
@ -1,44 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RecipeStep implements Model {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||
private Long id;
|
||||
|
||||
@NonNull
|
||||
@JsonIgnore
|
||||
@ManyToOne
|
||||
private Recipe recipe;
|
||||
|
||||
@NonNull
|
||||
@NotNull
|
||||
private String message;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RecipeStep that = (RecipeStep) o;
|
||||
return Objects.equals(recipe, that.recipe) &&
|
||||
Objects.equals(message, that.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(recipe, message);
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository;
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe;
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStep;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StepRepository extends JpaRepository<RecipeStep, Long> {
|
||||
|
||||
List<RecipeStep> findAllByRecipe(Recipe recipe);
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ public class RecipeService extends AbstractJavaService<Recipe, RecipeRepository>
|
|||
|
||||
private CompanyJavaService companyService;
|
||||
private MixService mixService;
|
||||
private StepService stepService;
|
||||
private RecipeStepJavaService stepService;
|
||||
private ImagesService imagesService;
|
||||
|
||||
public RecipeService() {
|
||||
|
@ -44,7 +44,7 @@ public class RecipeService extends AbstractJavaService<Recipe, RecipeRepository>
|
|||
}
|
||||
|
||||
@Autowired
|
||||
public void setStepService(StepService stepService) {
|
||||
public void setStepService(RecipeStepJavaService stepService) {
|
||||
this.stepService = stepService;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package dev.fyloz.trial.colorrecipesexplorer.service.model;
|
|||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe;
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStep;
|
||||
import dev.fyloz.trial.colorrecipesexplorer.repository.RecipeStepRepository;
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.AbstractJavaService;
|
||||
import dev.fyloz.trial.colorrecipesexplorer.repository.StepRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -12,15 +12,15 @@ import java.util.List;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class StepService extends AbstractJavaService<RecipeStep, StepRepository> {
|
||||
public class RecipeStepJavaService extends AbstractJavaService<RecipeStep, RecipeStepRepository> {
|
||||
|
||||
public StepService() {
|
||||
public RecipeStepJavaService() {
|
||||
super(RecipeStep.class);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setStepDao(StepRepository stepRepository) {
|
||||
this.repository = stepRepository;
|
||||
public void setStepDao(RecipeStepRepository recipeStepRepository) {
|
||||
this.repository = recipeStepRepository;
|
||||
}
|
||||
|
||||
/**
|
|
@ -53,9 +53,10 @@ import javax.servlet.http.HttpServletResponse
|
|||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@EnableConfigurationProperties(SecurityConfigurationProperties::class)
|
||||
class WebSecurityConfig(
|
||||
val restAuthenticationEntryPoint: RestAuthenticationEntryPoint,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties,
|
||||
val logger: Logger) : WebSecurityConfigurerAdapter() {
|
||||
val restAuthenticationEntryPoint: RestAuthenticationEntryPoint,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties,
|
||||
val logger: Logger
|
||||
) : WebSecurityConfigurerAdapter() {
|
||||
@Autowired
|
||||
private lateinit var userDetailsService: EmployeeUserDetailsServiceImpl
|
||||
|
||||
|
@ -82,12 +83,12 @@ class WebSecurityConfig(
|
|||
registerCorsConfiguration("/**", CorsConfiguration().apply {
|
||||
allowedOrigins = listOf("http://localhost:4200") // Angular development server
|
||||
allowedMethods = listOf(
|
||||
HttpMethod.GET.name,
|
||||
HttpMethod.POST.name,
|
||||
HttpMethod.PUT.name,
|
||||
HttpMethod.DELETE.name,
|
||||
HttpMethod.OPTIONS.name,
|
||||
HttpMethod.HEAD.name
|
||||
HttpMethod.GET.name,
|
||||
HttpMethod.POST.name,
|
||||
HttpMethod.PUT.name,
|
||||
HttpMethod.DELETE.name,
|
||||
HttpMethod.OPTIONS.name,
|
||||
HttpMethod.HEAD.name
|
||||
)
|
||||
allowCredentials = true
|
||||
}.applyPermitDefaultValues())
|
||||
|
@ -96,20 +97,27 @@ class WebSecurityConfig(
|
|||
|
||||
@PostConstruct
|
||||
fun createSystemUsers() {
|
||||
fun createUser(credentials: SecurityConfigurationProperties.SystemUserCredentials?, firstName: String, lastName: String, permissions: List<EmployeePermission>) {
|
||||
fun createUser(
|
||||
credentials: SecurityConfigurationProperties.SystemUserCredentials?,
|
||||
firstName: String,
|
||||
lastName: String,
|
||||
permissions: List<EmployeePermission>
|
||||
) {
|
||||
Assert.notNull(credentials, "No root user has been defined.")
|
||||
credentials!!
|
||||
Assert.notNull(credentials.id, "The root user has no identifier defined.")
|
||||
Assert.notNull(credentials.password, "The root user has no password defined.")
|
||||
if (!employeeService.existsById(credentials.id!!)) {
|
||||
employeeService.save(Employee(
|
||||
employeeService.save(
|
||||
Employee(
|
||||
id = credentials.id!!,
|
||||
firstName = firstName,
|
||||
lastName = lastName,
|
||||
password = passwordEncoder().encode(credentials.password!!),
|
||||
isSystemUser = true,
|
||||
permissions = permissions.toMutableSet()
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,27 +138,43 @@ class WebSecurityConfig(
|
|||
}
|
||||
|
||||
http
|
||||
.cors()
|
||||
.and()
|
||||
.headers().frameOptions().disable()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers(HttpMethod.GET, "/").permitAll()
|
||||
.antMatchers("/api/login").permitAll()
|
||||
.antMatchers("/api/employee/logout").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/api/employee/current").authenticated()
|
||||
.generateAuthorizations()
|
||||
.and()
|
||||
.addFilter(JwtAuthenticationFilter(authenticationManager(), employeeService, securityConfigurationProperties))
|
||||
.addFilter(JwtAuthorizationFilter(userDetailsService, securityConfigurationProperties, authenticationManager()))
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.cors()
|
||||
.and()
|
||||
.headers().frameOptions().disable()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers(HttpMethod.GET, "/").permitAll()
|
||||
.antMatchers("/api/login").permitAll()
|
||||
.antMatchers("/api/employee/logout").permitAll()
|
||||
.antMatchers(HttpMethod.GET, "/api/employee/current").authenticated()
|
||||
.generateAuthorizations()
|
||||
.and()
|
||||
.addFilter(
|
||||
JwtAuthenticationFilter(
|
||||
authenticationManager(),
|
||||
employeeService,
|
||||
securityConfigurationProperties
|
||||
)
|
||||
)
|
||||
.addFilter(
|
||||
JwtAuthorizationFilter(
|
||||
userDetailsService,
|
||||
securityConfigurationProperties,
|
||||
authenticationManager()
|
||||
)
|
||||
)
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
class RestAuthenticationEntryPoint : AuthenticationEntryPoint {
|
||||
override fun commence(request: HttpServletRequest, response: HttpServletResponse, authException: AuthenticationException) = response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized")
|
||||
override fun commence(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
authException: AuthenticationException
|
||||
) = response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized")
|
||||
}
|
||||
|
||||
class CorsFilter : Filter {
|
||||
|
@ -172,9 +196,9 @@ const val defaultGroupCookieName = "Default-Group"
|
|||
val blacklistedJwtTokens = mutableListOf<String>()
|
||||
|
||||
class JwtAuthenticationFilter(
|
||||
val authManager: AuthenticationManager,
|
||||
val employeeService: EmployeeServiceImpl,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties
|
||||
val authManager: AuthenticationManager,
|
||||
val employeeService: EmployeeServiceImpl,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties
|
||||
) : UsernamePasswordAuthenticationFilter() {
|
||||
init {
|
||||
setFilterProcessesUrl("/api/login")
|
||||
|
@ -185,7 +209,12 @@ class JwtAuthenticationFilter(
|
|||
return authManager.authenticate(UsernamePasswordAuthenticationToken(loginRequest.id, loginRequest.password))
|
||||
}
|
||||
|
||||
override fun successfulAuthentication(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain, authResult: Authentication) {
|
||||
override fun successfulAuthentication(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
chain: FilterChain,
|
||||
authResult: Authentication
|
||||
) {
|
||||
val jwtSecret = securityConfigurationProperties.jwtSecret
|
||||
val jwtDuration = securityConfigurationProperties.jwtDuration
|
||||
Assert.notNull(jwtSecret, "No JWT secret has been defined.")
|
||||
|
@ -195,25 +224,29 @@ class JwtAuthenticationFilter(
|
|||
val expirationMs = System.currentTimeMillis() + jwtDuration!!
|
||||
val expirationDate = Date(expirationMs)
|
||||
val token = Jwts.builder()
|
||||
.setSubject(employeeId)
|
||||
.setExpiration(expirationDate)
|
||||
.signWith(SignatureAlgorithm.HS512, jwtSecret!!.toByteArray())
|
||||
.compact()
|
||||
.setSubject(employeeId)
|
||||
.setExpiration(expirationDate)
|
||||
.signWith(SignatureAlgorithm.HS512, jwtSecret!!.toByteArray())
|
||||
.compact()
|
||||
response.addHeader("Access-Control-Expose-Headers", "X-Authentication-Expiration")
|
||||
response.addHeader("Set-Cookie", "$authorizationCookieName=Bearer$token; Max-Age=${jwtDuration / 1000}; HttpOnly; Secure; SameSite=strict")
|
||||
response.addHeader(
|
||||
"Set-Cookie",
|
||||
"$authorizationCookieName=Bearer$token; Max-Age=${jwtDuration / 1000}; HttpOnly; Secure; SameSite=strict"
|
||||
)
|
||||
response.addHeader(authorizationCookieName, "Bearer $token")
|
||||
response.addHeader("X-Authentication-Expiration", "$expirationMs")
|
||||
}
|
||||
}
|
||||
|
||||
class JwtAuthorizationFilter(
|
||||
val userDetailsService: EmployeeUserDetailsServiceImpl,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties,
|
||||
authenticationManager: AuthenticationManager
|
||||
val userDetailsService: EmployeeUserDetailsServiceImpl,
|
||||
val securityConfigurationProperties: SecurityConfigurationProperties,
|
||||
authenticationManager: AuthenticationManager
|
||||
) : BasicAuthenticationFilter(authenticationManager) {
|
||||
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
|
||||
val authorizationCookie = WebUtils.getCookie(request, authorizationCookieName)
|
||||
val authorizationValue = if (authorizationCookie != null) authorizationCookie.value else request.getHeader(authorizationCookieName)
|
||||
val authorizationValue =
|
||||
if (authorizationCookie != null) authorizationCookie.value else request.getHeader(authorizationCookieName)
|
||||
if (authorizationValue != null && authorizationValue.startsWith("Bearer") && authorizationValue !in blacklistedJwtTokens) {
|
||||
val authenticationToken = getAuthentication(authorizationValue)
|
||||
SecurityContextHolder.getContext().authentication = authenticationToken
|
||||
|
@ -231,10 +264,10 @@ class JwtAuthorizationFilter(
|
|||
val jwtSecret = securityConfigurationProperties.jwtSecret
|
||||
Assert.notNull(jwtSecret, "No JWT secret has been defined.")
|
||||
val employeeId = Jwts.parser()
|
||||
.setSigningKey(jwtSecret!!.toByteArray())
|
||||
.parseClaimsJws(token.replace("Bearer", ""))
|
||||
.body
|
||||
.subject
|
||||
.setSigningKey(jwtSecret!!.toByteArray())
|
||||
.parseClaimsJws(token.replace("Bearer", ""))
|
||||
.body
|
||||
.subject
|
||||
return if (employeeId != null) getAuthenticationToken(employeeId) else null
|
||||
}
|
||||
|
||||
|
@ -254,47 +287,71 @@ class SecurityConfigurationProperties {
|
|||
}
|
||||
|
||||
private enum class ControllerAuthorizations(
|
||||
val antMatcher: String,
|
||||
val permissions: Map<HttpMethod, EmployeePermission>
|
||||
val antMatcher: String,
|
||||
val permissions: Map<HttpMethod, EmployeePermission>
|
||||
) {
|
||||
MATERIALS("/api/material/**", mapOf(
|
||||
MATERIALS(
|
||||
"/api/material/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_MATERIAL,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_MATERIAL,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_MATERIAL,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_MATERIAL
|
||||
)),
|
||||
MATERIAL_TYPES("/api/materialtype/**", mapOf(
|
||||
)
|
||||
),
|
||||
MATERIAL_TYPES(
|
||||
"/api/materialtype/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_MATERIAL_TYPE,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_MATERIAL_TYPE,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_MATERIAL_TYPE,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_MATERIAL_TYPE
|
||||
)),
|
||||
COMPANY("/api/company/**", mapOf(
|
||||
)
|
||||
),
|
||||
COMPANY(
|
||||
"/api/company/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_COMPANY,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_COMPANY,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_COMPANY,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_COMPANY
|
||||
)),
|
||||
SET_BROWSER_DEFAULT_GROUP("/api/employee/group/default/**", mapOf(
|
||||
)
|
||||
),
|
||||
RECIPE_STEP(
|
||||
"/api/recipe/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_RECIPE,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_RECIPE,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_RECIPE,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_RECIPE
|
||||
)
|
||||
),
|
||||
SET_BROWSER_DEFAULT_GROUP(
|
||||
"/api/employee/group/default/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_EMPLOYEE_GROUP,
|
||||
HttpMethod.POST to EmployeePermission.SET_BROWSER_DEFAULT_GROUP
|
||||
)),
|
||||
EMPLOYEES_FOR_GROUP("/api/employee/group/*/employees", mapOf(
|
||||
)
|
||||
),
|
||||
EMPLOYEES_FOR_GROUP(
|
||||
"/api/employee/group/*/employees", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_EMPLOYEE
|
||||
)),
|
||||
EMPLOYEE_GROUP("/api/employee/group/**", mapOf(
|
||||
)
|
||||
),
|
||||
EMPLOYEE_GROUP(
|
||||
"/api/employee/group/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_EMPLOYEE_GROUP,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_EMPLOYEE_GROUP,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_EMPLOYEE_GROUP,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_EMPLOYEE_GROUP
|
||||
)),
|
||||
EMPLOYEE_PASSWORD("/api/employee/*/password", mapOf(
|
||||
)
|
||||
),
|
||||
EMPLOYEE_PASSWORD(
|
||||
"/api/employee/*/password", mapOf(
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_EMPLOYEE_PASSWORD
|
||||
)),
|
||||
EMPLOYEE("/api/employee/**", mapOf(
|
||||
)
|
||||
),
|
||||
EMPLOYEE(
|
||||
"/api/employee/**", mapOf(
|
||||
HttpMethod.GET to EmployeePermission.VIEW_EMPLOYEE,
|
||||
HttpMethod.POST to EmployeePermission.EDIT_EMPLOYEE,
|
||||
HttpMethod.PUT to EmployeePermission.EDIT_EMPLOYEE,
|
||||
HttpMethod.DELETE to EmployeePermission.REMOVE_EMPLOYEE
|
||||
))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -24,82 +24,93 @@ private const val EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit co
|
|||
|
||||
@Entity
|
||||
data class Employee(
|
||||
@Id
|
||||
override val id: Long,
|
||||
@Id
|
||||
override val id: Long,
|
||||
|
||||
val firstName: String = "",
|
||||
val firstName: String = "",
|
||||
|
||||
val lastName: String = "",
|
||||
val lastName: String = "",
|
||||
|
||||
@JsonIgnore
|
||||
val password: String = "",
|
||||
@JsonIgnore
|
||||
val password: String = "",
|
||||
|
||||
@JsonIgnore
|
||||
val isDefaultGroupUser: Boolean = false,
|
||||
@JsonIgnore
|
||||
val isDefaultGroupUser: Boolean = false,
|
||||
|
||||
@JsonIgnore
|
||||
val isSystemUser: Boolean = false,
|
||||
@JsonIgnore
|
||||
val isSystemUser: Boolean = false,
|
||||
|
||||
@field:ManyToOne
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var group: EmployeeGroup? = null,
|
||||
@field:ManyToOne
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var group: EmployeeGroup? = null,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@get:JsonIgnore
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@get:JsonIgnore
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
|
||||
val lastLoginTime: LocalDateTime? = null
|
||||
val lastLoginTime: LocalDateTime? = null
|
||||
) : Model {
|
||||
@JsonProperty("permissions")
|
||||
fun getFlattenedPermissions(): Iterable<EmployeePermission> = getPermissions()
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is Employee && id == other.id && firstName == other.firstName && lastName == other.lastName
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is Employee && id == other.id && firstName == other.firstName && lastName == other.lastName
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(id, firstName, lastName)
|
||||
}
|
||||
|
||||
/** DTO for creating employees. Allows a [password] a [groupId]. */
|
||||
open class EmployeeSaveDto(
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String,
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String,
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE)
|
||||
@field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE)
|
||||
val password: String,
|
||||
@field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE)
|
||||
@field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE)
|
||||
val password: String,
|
||||
|
||||
@field:ManyToOne
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var groupId: Long? = null,
|
||||
@field:ManyToOne
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var groupId: Long? = null,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf()
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf()
|
||||
) : EntityDto<Employee> {
|
||||
override fun toEntity(): Employee =
|
||||
Employee(id, firstName, lastName, "", isDefaultGroupUser = false, isSystemUser = false, group = null, permissions = permissions)
|
||||
Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
"",
|
||||
isDefaultGroupUser = false,
|
||||
isSystemUser = false,
|
||||
group = null,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
|
||||
open class EmployeeUpdateDto(
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String = "",
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String = "",
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String = "",
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String = "",
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: Set<EmployeePermission> = mutableSetOf()
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: Set<EmployeePermission> = mutableSetOf()
|
||||
) : EntityDto<Employee> {
|
||||
override fun toEntity(): Employee =
|
||||
Employee(id, firstName, lastName, permissions = permissions.toMutableSet())
|
||||
Employee(id, firstName, lastName, permissions = permissions.toMutableSet())
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,20 +120,20 @@ private const val GROUP_PERMISSIONS_EMPTY_MESSAGE = "Au moins une permission est
|
|||
|
||||
@Entity
|
||||
data class EmployeeGroup(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
override var id: Long? = null,
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
override var id: Long? = null,
|
||||
|
||||
@Column(unique = true)
|
||||
override val name: String = "",
|
||||
@Column(unique = true)
|
||||
override val name: String = "",
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
|
||||
@OneToMany
|
||||
@JsonIgnore
|
||||
val employees: MutableSet<Employee> = mutableSetOf()
|
||||
@OneToMany
|
||||
@JsonIgnore
|
||||
val employees: MutableSet<Employee> = mutableSetOf()
|
||||
) : NamedModel {
|
||||
fun getEmployeeCount() = employees.size
|
||||
|
||||
|
@ -131,30 +142,30 @@ data class EmployeeGroup(
|
|||
}
|
||||
|
||||
open class EmployeeGroupSaveDto(
|
||||
@field:NotBlank(message = GROUP_NAME_NULL_MESSAGE)
|
||||
@field:Size(min = 3)
|
||||
val name: String,
|
||||
@field:NotBlank(message = GROUP_NAME_NULL_MESSAGE)
|
||||
@field:Size(min = 3)
|
||||
val name: String,
|
||||
|
||||
@field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE)
|
||||
val permissions: MutableSet<EmployeePermission>
|
||||
@field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE)
|
||||
val permissions: MutableSet<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
override fun toEntity(): EmployeeGroup =
|
||||
EmployeeGroup(null, name, permissions)
|
||||
EmployeeGroup(null, name, permissions)
|
||||
}
|
||||
|
||||
open class EmployeeGroupUpdateDto(
|
||||
@field:NotNull(message = GROUP_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = GROUP_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NotBlank(message = GROUP_NAME_NULL_MESSAGE)
|
||||
@field:Size(min = 3)
|
||||
val name: String,
|
||||
@field:NotBlank(message = GROUP_NAME_NULL_MESSAGE)
|
||||
@field:Size(min = 3)
|
||||
val name: String,
|
||||
|
||||
@field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE)
|
||||
val permissions: MutableSet<EmployeePermission>
|
||||
@field:Size(min = 1, message = GROUP_PERMISSIONS_EMPTY_MESSAGE)
|
||||
val permissions: MutableSet<EmployeePermission>
|
||||
) : EntityDto<EmployeeGroup> {
|
||||
override fun toEntity(): EmployeeGroup =
|
||||
EmployeeGroup(id, name, permissions)
|
||||
EmployeeGroup(id, name, permissions)
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,11 +177,15 @@ enum class EmployeePermission(val impliedPermissions: List<EmployeePermission> =
|
|||
VIEW_MATERIAL,
|
||||
VIEW_MATERIAL_TYPE,
|
||||
VIEW_COMPANY,
|
||||
VIEW(listOf(
|
||||
VIEW_RECIPE,
|
||||
VIEW(
|
||||
listOf(
|
||||
VIEW_MATERIAL,
|
||||
VIEW_MATERIAL_TYPE,
|
||||
VIEW_COMPANY
|
||||
)),
|
||||
VIEW_COMPANY,
|
||||
VIEW_RECIPE
|
||||
)
|
||||
),
|
||||
VIEW_EMPLOYEE,
|
||||
VIEW_EMPLOYEE_GROUP,
|
||||
|
||||
|
@ -178,12 +193,16 @@ enum class EmployeePermission(val impliedPermissions: List<EmployeePermission> =
|
|||
EDIT_MATERIAL(listOf(VIEW_MATERIAL)),
|
||||
EDIT_MATERIAL_TYPE(listOf(VIEW_MATERIAL_TYPE)),
|
||||
EDIT_COMPANY(listOf(VIEW_COMPANY)),
|
||||
EDIT(listOf(
|
||||
EDIT_RECIPE(listOf(VIEW_RECIPE)),
|
||||
EDIT(
|
||||
listOf(
|
||||
EDIT_MATERIAL,
|
||||
EDIT_MATERIAL_TYPE,
|
||||
EDIT_COMPANY,
|
||||
EDIT_RECIPE,
|
||||
VIEW
|
||||
)),
|
||||
)
|
||||
),
|
||||
EDIT_EMPLOYEE(listOf(VIEW_EMPLOYEE)),
|
||||
EDIT_EMPLOYEE_PASSWORD(listOf(EDIT_EMPLOYEE)),
|
||||
EDIT_EMPLOYEE_GROUP(listOf(VIEW_EMPLOYEE_GROUP)),
|
||||
|
@ -192,21 +211,28 @@ enum class EmployeePermission(val impliedPermissions: List<EmployeePermission> =
|
|||
REMOVE_MATERIAL(listOf(EDIT_MATERIAL)),
|
||||
REMOVE_MATERIAL_TYPE(listOf(EDIT_MATERIAL_TYPE)),
|
||||
REMOVE_COMPANY(listOf(EDIT_COMPANY)),
|
||||
REMOVE(listOf(
|
||||
REMOVE_RECIPE(listOf(EDIT_RECIPE)),
|
||||
REMOVE(
|
||||
listOf(
|
||||
REMOVE_MATERIAL,
|
||||
REMOVE_MATERIAL_TYPE,
|
||||
REMOVE_COMPANY,
|
||||
REMOVE_RECIPE,
|
||||
EDIT
|
||||
)),
|
||||
)
|
||||
),
|
||||
REMOVE_EMPLOYEE(listOf(EDIT_EMPLOYEE)),
|
||||
REMOVE_EMPLOYEE_GROUP(listOf(EDIT_EMPLOYEE_GROUP)),
|
||||
|
||||
// Others
|
||||
SET_BROWSER_DEFAULT_GROUP(listOf(
|
||||
SET_BROWSER_DEFAULT_GROUP(
|
||||
listOf(
|
||||
VIEW_EMPLOYEE_GROUP
|
||||
)),
|
||||
)
|
||||
),
|
||||
|
||||
ADMIN(listOf(
|
||||
ADMIN(
|
||||
listOf(
|
||||
REMOVE,
|
||||
SET_BROWSER_DEFAULT_GROUP,
|
||||
|
||||
|
@ -214,7 +240,8 @@ enum class EmployeePermission(val impliedPermissions: List<EmployeePermission> =
|
|||
REMOVE_EMPLOYEE,
|
||||
EDIT_EMPLOYEE_PASSWORD,
|
||||
REMOVE_EMPLOYEE_GROUP,
|
||||
));
|
||||
)
|
||||
);
|
||||
|
||||
operator fun contains(permission: EmployeePermission): Boolean {
|
||||
return permission == this || impliedPermissions.any { permission in it }
|
||||
|
@ -250,70 +277,82 @@ private fun EmployeePermission.toAuthority(): GrantedAuthority {
|
|||
|
||||
// ==== DSL ====
|
||||
fun employee(
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
isDefaultGroupUser: Boolean = false,
|
||||
isSystemUser: Boolean = false,
|
||||
group: EmployeeGroup? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
lastLoginTime: LocalDateTime? = null,
|
||||
op: Employee.() -> Unit = {}
|
||||
) = Employee(id, firstName, lastName, password, isDefaultGroupUser, isSystemUser, group, permissions, lastLoginTime).apply(op)
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
isDefaultGroupUser: Boolean = false,
|
||||
isSystemUser: Boolean = false,
|
||||
group: EmployeeGroup? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
lastLoginTime: LocalDateTime? = null,
|
||||
op: Employee.() -> Unit = {}
|
||||
) = Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
password,
|
||||
isDefaultGroupUser,
|
||||
isSystemUser,
|
||||
group,
|
||||
permissions,
|
||||
lastLoginTime
|
||||
).apply(op)
|
||||
|
||||
fun employee(
|
||||
employee: Employee,
|
||||
newId: Long? = null
|
||||
employee: Employee,
|
||||
newId: Long? = null
|
||||
) = with(employee) {
|
||||
Employee(newId
|
||||
?: id, firstName, lastName, password, isDefaultGroupUser, isSystemUser, group, permissions, lastLoginTime)
|
||||
Employee(
|
||||
newId
|
||||
?: id, firstName, lastName, password, isDefaultGroupUser, isSystemUser, group, permissions, lastLoginTime
|
||||
)
|
||||
}
|
||||
|
||||
fun employeeSaveDto(
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeSaveDto.() -> Unit = {}
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeSaveDto.() -> Unit = {}
|
||||
) = EmployeeSaveDto(id, firstName, lastName, password, groupId, permissions).apply(op)
|
||||
|
||||
fun employeeUpdateDto(
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeUpdateDto.() -> Unit = {}
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeUpdateDto.() -> Unit = {}
|
||||
) = EmployeeUpdateDto(id, firstName, lastName, permissions).apply(op)
|
||||
|
||||
fun employeeGroup(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
employees: MutableSet<Employee> = mutableSetOf(),
|
||||
op: EmployeeGroup.() -> Unit = {}
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
employees: MutableSet<Employee> = mutableSetOf(),
|
||||
op: EmployeeGroup.() -> Unit = {}
|
||||
) = EmployeeGroup(id, name, permissions, employees).apply(op)
|
||||
|
||||
fun employeeGroup(
|
||||
employeeGroup: EmployeeGroup,
|
||||
newId: Long? = null,
|
||||
newName: String? = null
|
||||
employeeGroup: EmployeeGroup,
|
||||
newId: Long? = null,
|
||||
newName: String? = null
|
||||
) = with(employeeGroup) { EmployeeGroup(newId ?: id, newName ?: name, permissions, employees) }
|
||||
|
||||
fun employeeGroupSaveDto(
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupSaveDto.() -> Unit = {}
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupSaveDto.() -> Unit = {}
|
||||
) = EmployeeGroupSaveDto(name, permissions).apply(op)
|
||||
|
||||
fun employeeGroupUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupUpdateDto.() -> Unit = {}
|
||||
id: Long = 0L,
|
||||
name: String = "name",
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeGroupUpdateDto.() -> Unit = {}
|
||||
) = EmployeeGroupUpdateDto(id, name, permissions).apply(op)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.model
|
||||
|
||||
/** The model of a stored entity. Each model should implements its own equals and hashCode methods to keep compatibility with the legacy Java and Thymeleaf code. */
|
||||
interface Model {
|
||||
val id: Long?
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
import javax.validation.constraints.NotNull
|
||||
|
||||
private const val RECIPE_STEP_ID_NULL_MESSAGE = "Un identifiant est requis"
|
||||
private const val RECIPE_STEP_RECIPE_NULL_MESSAGE = "Une recette est requise"
|
||||
private const val RECIPE_STEP_MESSAGE_NULL_MESSAGE = "Un message est requis"
|
||||
|
||||
@Entity
|
||||
data class RecipeStep(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||
override val id: Long?,
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne
|
||||
val recipe: Recipe?,
|
||||
|
||||
val message: String
|
||||
) : Model {
|
||||
constructor(recipe: Recipe?, message: String) : this(null, recipe, message)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is RecipeStep && other.recipe == recipe && other.message == message
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(recipe, message)
|
||||
}
|
||||
|
||||
|
||||
open class RecipeStepSaveDto(
|
||||
@field:NotNull(message = RECIPE_STEP_RECIPE_NULL_MESSAGE)
|
||||
val recipe: Recipe,
|
||||
|
||||
@field:NotNull(message = RECIPE_STEP_MESSAGE_NULL_MESSAGE)
|
||||
val message: String
|
||||
) : EntityDto<RecipeStep> {
|
||||
override fun toEntity(): RecipeStep = RecipeStep(null, recipe, message)
|
||||
}
|
||||
|
||||
|
||||
open class RecipeStepUpdateDto(
|
||||
@field:NotNull(message = RECIPE_STEP_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
val recipe: Recipe?,
|
||||
|
||||
@field:NullOrNotBlank(message = RECIPE_STEP_MESSAGE_NULL_MESSAGE)
|
||||
val message: String?
|
||||
) : EntityDto<RecipeStep> {
|
||||
override fun toEntity(): RecipeStep = RecipeStep(id, recipe, message ?: "")
|
||||
}
|
||||
|
||||
// ==== DSL ====
|
||||
fun recipeStep(
|
||||
id: Long? = null,
|
||||
recipe: Recipe? = TODO(), // TODO change default when recipe DSL is done
|
||||
message: String = "message",
|
||||
op: RecipeStep.() -> Unit = {}
|
||||
) = RecipeStep(id, recipe, message).apply(op)
|
||||
|
||||
fun recipeStepSaveDto(
|
||||
recipe: Recipe = TODO(), // TODO change default when recipe DSL is done
|
||||
message: String = "message",
|
||||
op: RecipeStepSaveDto.() -> Unit = {}
|
||||
) = RecipeStepSaveDto(recipe, message).apply(op)
|
||||
|
||||
fun recipeStepUpdateDto(
|
||||
id: Long = 0L,
|
||||
recipe: Recipe? = TODO(),
|
||||
message: String? = "message",
|
||||
op: RecipeStepUpdateDto.() -> Unit = {}
|
||||
) = RecipeStepUpdateDto(id, recipe, message).apply(op)
|
|
@ -0,0 +1,8 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStep
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
interface RecipeStepRepository : JpaRepository<RecipeStep, Long>
|
|
@ -13,4 +13,8 @@ private const val COMPANY_CONTROLLER_PATH = "api/company"
|
|||
@RestController
|
||||
@RequestMapping(COMPANY_CONTROLLER_PATH)
|
||||
@Profile("rest")
|
||||
class CompanyController(companyService: CompanyService) : AbstractRestModelApiController<Company, CompanySaveDto, CompanyUpdateDto, CompanyService>(companyService, COMPANY_CONTROLLER_PATH)
|
||||
class CompanyController(companyService: CompanyService) :
|
||||
AbstractRestModelApiController<Company, CompanySaveDto, CompanyUpdateDto, CompanyService>(
|
||||
companyService,
|
||||
COMPANY_CONTROLLER_PATH
|
||||
)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.rest
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.InventoryMaterial
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.InventoryService
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
class InventoryController(val inventoryService: InventoryService) {
|
||||
fun getAllMaterials(): ResponseEntity<Collection<InventoryMaterial>> = ResponseEntity.ok(inventoryService.getAllMaterials())
|
||||
}
|
||||
//package dev.fyloz.trial.colorrecipesexplorer.rest
|
||||
//
|
||||
//import dev.fyloz.trial.colorrecipesexplorer.model.InventoryMaterial
|
||||
//import dev.fyloz.trial.colorrecipesexplorer.service.InventoryService
|
||||
//import org.springframework.http.ResponseEntity
|
||||
//import org.springframework.web.bind.annotation.RestController
|
||||
//
|
||||
//@RestController
|
||||
//class InventoryController(val inventoryService: InventoryService) {
|
||||
// fun getAllMaterials(): ResponseEntity<Collection<InventoryMaterial>> = ResponseEntity.ok(inventoryService.getAllMaterials())
|
||||
//}
|
||||
|
|
|
@ -13,8 +13,5 @@ private const val MATERIAL_TYPE_CONTROLLER_PATH = "api/materialtype"
|
|||
|
||||
@RestController
|
||||
@RequestMapping(MATERIAL_TYPE_CONTROLLER_PATH)
|
||||
@Profile("rest")
|
||||
class MaterialTypeController(materialTypeService: MaterialTypeService) :
|
||||
AbstractRestModelApiController<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeService>(materialTypeService, MATERIAL_TYPE_CONTROLLER_PATH) {
|
||||
|
||||
}
|
||||
AbstractRestModelApiController<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeService>(materialTypeService, MATERIAL_TYPE_CONTROLLER_PATH)
|
||||
|
|
|
@ -4,6 +4,7 @@ import dev.fyloz.trial.colorrecipesexplorer.model.EntityDto
|
|||
import dev.fyloz.trial.colorrecipesexplorer.model.Model
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.ModelService
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.Service
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.service
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStep
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStepSaveDto
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStepUpdateDto
|
||||
import dev.fyloz.trial.colorrecipesexplorer.repository.RecipeStepRepository
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
||||
interface RecipeStepService : ModelService<RecipeStep, RecipeStepSaveDto, RecipeStepUpdateDto, RecipeStepRepository> {
|
||||
/** Creates a step for the given [recipe] with the given [message]. */
|
||||
fun createForRecipe(recipe: Recipe, message: String): RecipeStep
|
||||
|
||||
/** Creates several steps for the given [recipe] with the given [messages]. */
|
||||
fun createAllForRecipe(recipe: Recipe, messages: Collection<String>): Collection<RecipeStep>
|
||||
}
|
||||
|
||||
@Service
|
||||
class RecipeStepServiceImpl(recipeStepRepository: RecipeStepRepository) :
|
||||
AbstractModelService<RecipeStep, RecipeStepSaveDto, RecipeStepUpdateDto, RecipeStepRepository>(recipeStepRepository),
|
||||
RecipeStepService {
|
||||
override fun createForRecipe(recipe: Recipe, message: String): RecipeStep =
|
||||
RecipeStep(recipe, message)
|
||||
|
||||
@Transactional
|
||||
override fun createAllForRecipe(recipe: Recipe, messages: Collection<String>): Collection<RecipeStep> =
|
||||
messages.map { createForRecipe(recipe, it) }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
|
||||
@DataJpaTest
|
||||
class RecipeStepRepositoryTest @Autowired constructor(
|
||||
recipeStepRepository: RecipeStepRepository,
|
||||
entityManager: TestEntityManager
|
||||
) {
|
||||
// Nothing for now
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.service
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.spy
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.trial.colorrecipesexplorer.repository.RecipeStepRepository
|
||||
import org.junit.jupiter.api.Nested
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class RecipeStepServiceTest :
|
||||
AbstractModelServiceTest<RecipeStep, RecipeStepSaveDto, RecipeStepUpdateDto, RecipeStepService, RecipeStepRepository>() {
|
||||
override val repository: RecipeStepRepository = mock()
|
||||
override val service: RecipeStepService = spy(RecipeStepServiceImpl(repository))
|
||||
|
||||
override val entity: RecipeStep = recipeStep(id = 0L, recipe = TODO(), message = "message")
|
||||
override val anotherEntity: RecipeStep = recipeStep(id = 1L, recipe = TODO(), message = "another message")
|
||||
override val entitySaveDto: RecipeStepSaveDto = spy(recipeStepSaveDto(entity.recipe!!, entity.message))
|
||||
override val entityUpdateDto: RecipeStepUpdateDto =
|
||||
spy(recipeStepUpdateDto(entity.id!!, entity.recipe, entity.message))
|
||||
|
||||
@Nested
|
||||
inner class CreateForRecipe {
|
||||
fun `returns a correct RecipeStep`() {
|
||||
val step = recipeStep(null, entity.recipe, entity.message)
|
||||
|
||||
val found = service.createForRecipe(entity.recipe!!, entity.message)
|
||||
|
||||
assertEquals(step, found)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class CreateAllForRecipe {
|
||||
fun `returns all correct RecipeSteps`() {
|
||||
val steps = listOf(
|
||||
recipeStep(null, entity.recipe, entity.message),
|
||||
recipeStep(null, entity.recipe, anotherEntity.message)
|
||||
)
|
||||
val messages = steps.map { it.message }
|
||||
|
||||
val found = service.createAllForRecipe(entity.recipe!!, messages)
|
||||
|
||||
assertEquals(steps, found)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue