diff --git a/pom.xml b/pom.xml index 453ec3b..1c579ab 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.5 + 3.3.0 @@ -24,7 +24,7 @@ ${basedir}/target/site/jacoco/** 42.7.3 3.10.0 - 0.9.1 + 0.12.5 1.18.32 1.19.8 3.1.0 @@ -34,8 +34,6 @@ 5.1.0 1.4.9 0.8.9 - 2.16.0 - 9.37.3 @@ -59,13 +57,6 @@ spring-boot-starter-validation - - - com.nimbusds - nimbus-jose-jwt - ${nimbus-jose-jwt.version} - - org.springframework.boot spring-boot-starter-cache @@ -103,6 +94,11 @@ flyway-core + + org.flywaydb + flyway-database-postgresql + + commons-net commons-net @@ -111,20 +107,22 @@ io.jsonwebtoken - jjwt + jjwt-api + ${jjwt.version} + + + + io.jsonwebtoken + jjwt-impl ${jjwt.version} - - - com.fasterxml.jackson.core - jackson-databind - - + runtime - com.fasterxml.jackson.core - jackson-databind - ${jackson-databind.version} + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime @@ -133,6 +131,8 @@ ${lombok.version} + + org.springframework.security spring-security-test @@ -166,12 +166,6 @@ test - - javax.xml.bind - jaxb-api - ${jaxb-api.version} - - diff --git a/postman-collection/ecordel.postman_collection.json b/postman-collection/ecordel.postman_collection.json new file mode 100644 index 0000000..37052f0 --- /dev/null +++ b/postman-collection/ecordel.postman_collection.json @@ -0,0 +1,405 @@ +{ + "info": { + "_postman_id": "6c24e3de-35cb-4bb2-8dd9-60f05eaffd0a", + "name": "ecordel", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "cordels", + "item": [ + { + "name": "cordels", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{ecordel_url}}/cordels/summaries?size=30&page=0&authorId=3", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "cordels", + "summaries" + ], + "query": [ + { + "key": "size", + "value": "30" + }, + { + "key": "page", + "value": "0" + }, + { + "key": "authorId", + "value": "3" + } + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + }, + { + "name": "cordels/{id}", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ecordel_url}}/cordels/39", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "cordels", + "39" + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + }, + { + "name": "new cordel", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"author\": {\"id\": 1},\n \"title\": \"Titulo\",\n \"xilogravura\": \"\",\n \"description\": \"Descrição\",\n \"content\":\"Conteúdo\\nConteúdo\"\n}" + }, + "url": { + "raw": "{{ecordel_url}}/cordels", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "cordels" + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + }, + { + "name": "put xilogravura", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{ecordel_url}}/cordels/1/xilogravura", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "cordels", + "1", + "xilogravura" + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + } + ] + }, + { + "name": "authors", + "item": [ + { + "name": "new author", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\": \"Author Test\",\n \"about\": \"About author test\",\n \"email\": \"author.test@ecordel.com\"\n}" + }, + "url": { + "raw": "{{ecordel_url}}/authors", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "authors" + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + }, + { + "name": "authors", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{ecordel_url}}/authors", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "authors" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "auth", + "item": [ + { + "name": "login", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// set token collection variable\r", + "pm.collectionVariables.set(\"token\", pm.response.json().token);\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"username\":\"admin\",\n\t\"password\":\"admin\"\n}" + }, + "url": { + "raw": "{{ecordel_url}}/auth", + "host": [ + "{{ecordel_url}}" + ], + "path": [ + "auth" + ] + } + }, + "response": [ + { + "name": "Default", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{url}}/cordels", + "host": [ + "{{url}}" + ], + "path": [ + "cordels" + ] + } + }, + "code": 200, + "_postman_previewlanguage": "Text", + "header": [], + "cookie": [], + "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" + } + ] + } + ] + } + ], + "variable": [ + { + "key": "token", + "value": "" + } + ] +} \ No newline at end of file diff --git a/postman-collection/local.postman_environment.json b/postman-collection/local.postman_environment.json new file mode 100644 index 0000000..bc4b2f4 --- /dev/null +++ b/postman-collection/local.postman_environment.json @@ -0,0 +1,14 @@ +{ + "id": "15cbdb92-e67d-4bfc-b3b3-fa908575573c", + "name": "local", + "values": [ + { + "key": "ecordel_url", + "value": "http://localhost:5000/api/v1", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2024-06-01T10:51:35.494Z", + "_postman_exported_using": "Postman/10.17.3" +} \ No newline at end of file diff --git a/src/main/java/br/com/itsmemario/ecordel/EcordelApplication.java b/src/main/java/br/com/itsmemario/ecordel/EcordelApplication.java index 2acba16..72fe597 100644 --- a/src/main/java/br/com/itsmemario/ecordel/EcordelApplication.java +++ b/src/main/java/br/com/itsmemario/ecordel/EcordelApplication.java @@ -17,7 +17,6 @@ package br.com.itsmemario.ecordel; -import br.com.itsmemario.ecordel.security.jwt.JwtToken; import br.com.itsmemario.ecordel.security.jwt.JwtTokenService; import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; @@ -39,11 +38,6 @@ public static void main(String[] args) { SpringApplication.run(EcordelApplication.class, args); } - @Bean - public JwtToken jwtToken() { - return jwtTokenService.findTop(); - } - @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/src/main/java/br/com/itsmemario/ecordel/exception/DefaultExceptionHandler.java b/src/main/java/br/com/itsmemario/ecordel/exception/DefaultExceptionHandler.java index cee6051..5c46894 100644 --- a/src/main/java/br/com/itsmemario/ecordel/exception/DefaultExceptionHandler.java +++ b/src/main/java/br/com/itsmemario/ecordel/exception/DefaultExceptionHandler.java @@ -17,10 +17,7 @@ package br.com.itsmemario.ecordel.exception; -import java.util.List; -import java.util.stream.Collectors; - -import org.springframework.beans.factory.annotation.Autowired; +import lombok.AllArgsConstructor; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpStatus; @@ -30,10 +27,12 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.List; + @RestControllerAdvice +@AllArgsConstructor public class DefaultExceptionHandler { - @Autowired private MessageSource messageSource; @ResponseStatus(code=HttpStatus.BAD_REQUEST) @@ -46,7 +45,7 @@ public List handle(MethodArgumentNotValidException exception) { String msg = messageSource.getMessage(err, LocaleContextHolder.getLocale()); return new FormError(err.getField(), msg); } - ) .collect(Collectors.toList()); + ) .toList(); } } diff --git a/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationController.java b/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationController.java index d0c92d8..798fd47 100644 --- a/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationController.java +++ b/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationController.java @@ -48,14 +48,15 @@ public AuthenticationController(AuthenticationProvider provider, AuthenticationS } @PostMapping - public ResponseEntity authenticate(@RequestBody @Valid LoginData data){ - logger.info("Login attempt"); + public ResponseEntity authenticate(@RequestBody @Valid LoginData loginData){ + logger.info("Login attempt, user: {}", loginData.getUsername()); try { - UsernamePasswordAuthenticationToken authenticationToken = data.toAuthenticationToken(); + UsernamePasswordAuthenticationToken authenticationToken = loginData.toAuthenticationToken(); Authentication authentication = provider.authenticate(authenticationToken); TokenDto tokenDto = authenticationService.generateToken(authentication); return ResponseEntity.ok(tokenDto); } catch (AuthenticationException e) { + logger.info("Authentication failed for user: {}", loginData.getUsername()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } diff --git a/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationService.java b/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationService.java index fa17b3d..98b5356 100644 --- a/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationService.java +++ b/src/main/java/br/com/itsmemario/ecordel/security/AuthenticationService.java @@ -17,10 +17,13 @@ package br.com.itsmemario.ecordel.security; -import br.com.itsmemario.ecordel.security.jwt.JwtToken; +import br.com.itsmemario.ecordel.security.jwt.JwtTokenService; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +33,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; +import javax.crypto.SecretKey; import java.util.Date; import java.util.Optional; @@ -39,12 +43,12 @@ public class AuthenticationService implements UserDetailsService { public static final String BEARER = "Bearer"; public static final String ROLES = "roles"; - private static final String ISSUER = "e-cordel"; + private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final UserRepository repository; - private final JwtToken jwtToken; + private final JwtTokenService tokenService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -58,43 +62,54 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx public TokenDto generateToken(Authentication authentication) { CordelUser principal = (CordelUser) authentication.getPrincipal(); - - Date today = new Date(); - Date expiration = new Date(today.getTime() + jwtToken.getExpiration()); - + Date expiration = calculateExpiration(); + String token = Jwts.builder() - .setIssuer(ISSUER) - .setSubject(principal.getId().toString()) + .issuer(ISSUER) + .subject(principal.getId().toString()) .claim(ROLES, principal.getAuthorityNames()) - .setIssuedAt(today) - .setExpiration(expiration) - .signWith(SignatureAlgorithm.HS256, jwtToken.getSecretKey()) + .issuedAt(new Date()) + .expiration(expiration) + .signWith(decodeSecretKey()) .compact(); return new TokenDto(token, BEARER, expiration.getTime()); } + private Date calculateExpiration() { + return new Date(new Date().getTime() + tokenService.findTop().getExpiration()); + } + public boolean isValidToken(String token) { try { - return Jwts.parser() - .setSigningKey(jwtToken.getSecretKey()) - .requireIssuer(ISSUER) - .parseClaimsJws(token)!=null; - }catch(io.jsonwebtoken.SignatureException e) { + return parseToken(token) != null; + } catch (SignatureException e) { return false; } } public Optional getUserFromToken(String token) { try { - Claims body = Jwts.parser().setSigningKey(jwtToken.getSecretKey()).parseClaimsJws(token).getBody(); + Claims body = parseToken(token).getPayload(); Long id = Long.parseLong(body.getSubject()); return repository.findById(id); - } catch (io.jsonwebtoken.SignatureException e) { - logger.warn("No possible to parser token: {}", e.getMessage()); + } catch (SignatureException e) { + logger.error("No possible to parser token: {}", e.getMessage()); return Optional.empty(); } } - - + + private Jws parseToken(String token) throws SignatureException { + return Jwts.parser() + .verifyWith(decodeSecretKey()) + .requireIssuer(ISSUER) + .build() + .parseSignedClaims(token); + } + + private SecretKey decodeSecretKey() { + byte[] keyBytes = Decoders.BASE64.decode(tokenService.findTop().getSecretKey()); + return Keys.hmacShaKeyFor(keyBytes); + } + } diff --git a/src/main/java/br/com/itsmemario/ecordel/security/SecurityConfig.java b/src/main/java/br/com/itsmemario/ecordel/security/SecurityConfig.java index 02a1ca4..b28303b 100644 --- a/src/main/java/br/com/itsmemario/ecordel/security/SecurityConfig.java +++ b/src/main/java/br/com/itsmemario/ecordel/security/SecurityConfig.java @@ -81,7 +81,7 @@ public WebSecurityCustomizer webSecurityCustomizer() { * @param args */ public static void main(String[] args) { - //System.out.println(new BCryptPasswordEncoder().encode("")); + // System.out.println(new BCryptPasswordEncoder().encode("admin")); } @Bean diff --git a/src/main/java/br/com/itsmemario/ecordel/security/jwt/JwtTokenService.java b/src/main/java/br/com/itsmemario/ecordel/security/jwt/JwtTokenService.java index 68f4225..e030b8a 100644 --- a/src/main/java/br/com/itsmemario/ecordel/security/jwt/JwtTokenService.java +++ b/src/main/java/br/com/itsmemario/ecordel/security/jwt/JwtTokenService.java @@ -18,16 +18,29 @@ package br.com.itsmemario.ecordel.security.jwt; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import java.util.concurrent.TimeUnit; + @Service @RequiredArgsConstructor public class JwtTokenService { + public static final String SECRET_KEY = "secretKey"; private final JwtTokenRepository repository; + @Cacheable(cacheNames = {SECRET_KEY}) public JwtToken findTop() { return repository.findFirstByOrderByIdDesc(); } + @CacheEvict(allEntries = true, cacheNames = { SECRET_KEY }) + @Scheduled(fixedDelay = 30, timeUnit = TimeUnit.MINUTES) + public void cacheEvict() { + // spring will call this method to perform cache eviction + } + } diff --git a/src/main/resources/db/migration/V1.0.1__update-secret-token.sql b/src/main/resources/db/migration/V1.0.1__update-secret-token.sql new file mode 100644 index 0000000..b3fd68d --- /dev/null +++ b/src/main/resources/db/migration/V1.0.1__update-secret-token.sql @@ -0,0 +1,4 @@ +-- update the initial value that has invalid characters for new jjwt version +-- this used only for testing and to guarantee the server will be up and running locally without any issues in local dev +-- the production value is not and MUST to be managed by flyway migrations +update jwt_token set secret_key = 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gU2VkIHRpbmNpZHVudCBhbnRlIGV0IG1hZ25hIHB1bHZpbmFyIGJpYmVuZHVtLiBEb25lYyBwdWx2aW5hciBkaWFtIGVnZXQgYXJjdSB2dWxwdXRhdGUgcG9zdWVyZS4gRHVpcyBzdXNjaXBpdCBpbiBmZWxpcyBldSB0ZW1wdXMuIERvbmVjIG5vbiBlZmZpY2l0dXIgb3JjaS4gU2VkIGFjIHNvbGxpY2l0dWRpbiBuaXNpLiBOYW0gbmliaCByaXN1cywgZmFjaWxpc2lzIHZpdGFlIGJpYmVuZHVtIHZpdGFlLCBlZmZpY2l0dXIgZXgu'; diff --git a/src/test/java/br/com/itsmemario/ecordel/AbstractIntegrationTest.java b/src/test/java/br/com/itsmemario/ecordel/AbstractIntegrationTest.java index 6bc2d19..e633657 100644 --- a/src/test/java/br/com/itsmemario/ecordel/AbstractIntegrationTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/AbstractIntegrationTest.java @@ -17,46 +17,36 @@ package br.com.itsmemario.ecordel; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.test.context.ContextConfiguration; +import org.junit.jupiter.api.BeforeAll; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.PostgreSQLContainer; -import org.testcontainers.lifecycle.Startables; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Stream; - -@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AbstractIntegrationTest { - static class Initializer implements ApplicationContextInitializer { - - static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:12.8"); - - private static void startContainers() { - Startables.deepStart(Stream.of(postgres)).join(); - } - - private static Map createConnectionConfiguration() { - Map map = new HashMap<>(); - map.put("spring.datasource.url", postgres.getJdbcUrl()); - map.put("spring.datasource.username", postgres.getUsername()); - map.put("spring.datasource.password", postgres.getPassword()); - return map; - } - - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - startContainers(); - ConfigurableEnvironment environment = applicationContext.getEnvironment(); - MapPropertySource testcontainers = new MapPropertySource( - "testcontainers", - createConnectionConfiguration() - ); - environment.getPropertySources().addFirst(testcontainers); - } + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:12.8").withReuse(true); + + @LocalServerPort + protected int port; + + @Autowired + protected TestRestTemplate restTemplate; + + @BeforeAll + static void beforeAll() { + postgres.start(); } + + @DynamicPropertySource + static void configureProperties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", postgres::getJdbcUrl); + registry.add("spring.datasource.username", postgres::getUsername); + registry.add("spring.datasource.password", postgres::getPassword); + } + } \ No newline at end of file diff --git a/src/test/java/br/com/itsmemario/ecordel/EcordelApplicationTests.java b/src/test/java/br/com/itsmemario/ecordel/EcordelApplicationTests.java index bf61f7b..ec27538 100644 --- a/src/test/java/br/com/itsmemario/ecordel/EcordelApplicationTests.java +++ b/src/test/java/br/com/itsmemario/ecordel/EcordelApplicationTests.java @@ -19,22 +19,17 @@ import br.com.itsmemario.ecordel.cordel.CordelService; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; -@ExtendWith(SpringExtension.class) -@SpringBootTest -public class EcordelApplicationTests extends AbstractIntegrationTest { +class EcordelApplicationTests extends AbstractIntegrationTest { @Autowired CordelService service; @Test - public void contextLoads() { + void contextLoads() { assertThat(service).isNotNull(); } diff --git a/src/test/java/br/com/itsmemario/ecordel/author/AuthorControllerTest.java b/src/test/java/br/com/itsmemario/ecordel/author/AuthorControllerTest.java index c9346d6..e327e8d 100644 --- a/src/test/java/br/com/itsmemario/ecordel/author/AuthorControllerTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/author/AuthorControllerTest.java @@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.test.context.jdbc.Sql; @@ -34,7 +33,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@SpringBootTest @AutoConfigureMockMvc class AuthorControllerTest extends AbstractIntegrationTest { diff --git a/src/test/java/br/com/itsmemario/ecordel/author/AuthorRepositoryTest.java b/src/test/java/br/com/itsmemario/ecordel/author/AuthorRepositoryTest.java index 4b14cbe..ac376f9 100644 --- a/src/test/java/br/com/itsmemario/ecordel/author/AuthorRepositoryTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/author/AuthorRepositoryTest.java @@ -21,14 +21,12 @@ import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @Slf4j -@SpringBootTest class AuthorRepositoryTest extends AbstractIntegrationTest { @Autowired diff --git a/src/test/java/br/com/itsmemario/ecordel/cordel/CordelControllerTest.java b/src/test/java/br/com/itsmemario/ecordel/cordel/CordelControllerTest.java index dbe9987..da93cf7 100644 --- a/src/test/java/br/com/itsmemario/ecordel/cordel/CordelControllerTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/cordel/CordelControllerTest.java @@ -23,9 +23,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -34,15 +31,8 @@ import static br.com.itsmemario.ecordel.cordel.CordelUtil.newCordel; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class CordelControllerTest extends AbstractIntegrationTest { - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate restTemplate; - @Autowired CordelRepository cordelRepository; diff --git a/src/test/java/br/com/itsmemario/ecordel/cordel/CordelRepositoryTest.java b/src/test/java/br/com/itsmemario/ecordel/cordel/CordelRepositoryTest.java index 43397d5..d55f01e 100644 --- a/src/test/java/br/com/itsmemario/ecordel/cordel/CordelRepositoryTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/cordel/CordelRepositoryTest.java @@ -3,15 +3,11 @@ import br.com.itsmemario.ecordel.AbstractIntegrationTest; import br.com.itsmemario.ecordel.author.Author; import br.com.itsmemario.ecordel.author.AuthorRepository; -import org.checkerframework.checker.units.qual.A; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.IOException; import java.nio.file.Files; @@ -23,8 +19,6 @@ import static br.com.itsmemario.ecordel.cordel.CordelUtil.newCordel; import static org.assertj.core.api.Assertions.assertThat; -@ExtendWith(SpringExtension.class) -@SpringBootTest class CordelRepositoryTest extends AbstractIntegrationTest { private static final Path BIG_FILE = Paths.get("src/test/resources/content.txt"); diff --git a/src/test/java/br/com/itsmemario/ecordel/file/FtpClientsTest.java b/src/test/java/br/com/itsmemario/ecordel/file/FtpClientsTest.java index 36ac7f8..4a2e1f6 100644 --- a/src/test/java/br/com/itsmemario/ecordel/file/FtpClientsTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/file/FtpClientsTest.java @@ -46,7 +46,7 @@ public class FtpClientsTest { static final FakeFtpServer fakeFtpServer = new FakeFtpServer(); @BeforeAll - public static void setup() throws IOException { + public static void setup() { setUpMockServer(); } diff --git a/src/test/java/br/com/itsmemario/ecordel/security/AuthenticationControllerTest.java b/src/test/java/br/com/itsmemario/ecordel/security/AuthenticationControllerTest.java index 2ab910f..5e17f71 100644 --- a/src/test/java/br/com/itsmemario/ecordel/security/AuthenticationControllerTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/security/AuthenticationControllerTest.java @@ -18,29 +18,23 @@ package br.com.itsmemario.ecordel.security; import br.com.itsmemario.ecordel.AbstractIntegrationTest; +import br.com.itsmemario.ecordel.security.jwt.JwtTokenService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class AuthenticationControllerTest extends AbstractIntegrationTest { - @LocalServerPort - private int port; - @Autowired - private TestRestTemplate restTemplate; + UserRepository repository; @Autowired - UserRepository repository; + JwtTokenService tokenService; @AfterEach void tearDown() { diff --git a/src/test/java/br/com/itsmemario/ecordel/xilogravura/XilogravuraServiceTest.java b/src/test/java/br/com/itsmemario/ecordel/xilogravura/XilogravuraServiceTest.java index 2377e11..73893e3 100644 --- a/src/test/java/br/com/itsmemario/ecordel/xilogravura/XilogravuraServiceTest.java +++ b/src/test/java/br/com/itsmemario/ecordel/xilogravura/XilogravuraServiceTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockftpserver.fake.FakeFtpServer; import org.mockftpserver.fake.UserAccount; import org.mockftpserver.fake.filesystem.DirectoryEntry; @@ -12,16 +11,10 @@ import org.mockftpserver.fake.filesystem.FileSystem; import org.mockftpserver.fake.filesystem.UnixFakeFileSystem; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockMultipartFile; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; -@ExtendWith(SpringExtension.class) -@SpringBootTest public class XilogravuraServiceTest extends AbstractIntegrationTest { public static final int PORT = 10021; @@ -34,7 +27,7 @@ public class XilogravuraServiceTest extends AbstractIntegrationTest { static final FakeFtpServer fakeFtpServer = new FakeFtpServer(); @BeforeAll - public static void setup() throws IOException { + public static void setup() { setUpMockServer(); } @@ -49,12 +42,12 @@ private static void setUpMockServer() { } @AfterAll - public static void afterClass() throws Exception { + public static void afterClass() { fakeFtpServer.stop(); } @Test - public void createXilogravuraWithFile() { + void createXilogravuraWithFile() { MockMultipartFile file = new MockMultipartFile("file.txt", "content".getBytes()); String xilogravuraUrl = service.createXilogravuraWithFile(file); diff --git a/src/test/resources/ecordel.postman_collection.json b/src/test/resources/ecordel.postman_collection.json deleted file mode 100644 index 9b37ac4..0000000 --- a/src/test/resources/ecordel.postman_collection.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "info": { - "_postman_id": "e5e94a1a-5377-484d-9916-1d99afc58116", - "name": "ecordel", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "cordels", - "item": [ - { - "name": "cordels", - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{ecordel_url}}/cordels?size=30&page=0", - "host": [ - "{{ecordel_url}}" - ], - "path": [ - "cordels" - ], - "query": [ - { - "key": "size", - "value": "30" - }, - { - "key": "page", - "value": "0" - } - ] - } - }, - "response": [ - { - "name": "Default", - "originalRequest": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}/cordels", - "host": [ - "{{url}}" - ], - "path": [ - "cordels" - ] - } - }, - "code": 200, - "_postman_previewlanguage": null, - "header": null, - "cookie": [], - "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" - } - ] - }, - { - "name": "new cordel", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJlLWNvcmRlbCIsInN1YiI6IjEiLCJpYXQiOjE1OTgwNDM4NjgsImV4cCI6MTU5ODEzMDI2OH0.X-giV2mOUr0A88cclVMsWEktI5uqV_8gV8YzBLGHb_Y", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"author\": {\"id\": 1},\n \"title\": \"Some new cordel\",\n \"xilogravura\": \"https://i.pinimg.com/originals/25/9d/47/259d47304bf26a4678cb039b8d8ce7f9.jpg\",\n \"description\": \"Cordel descrition\",\n \"content\":\"asdfas asdfasdf asd fasdf çasdfasd fasdfa sdlkfa sdfaklsd faksdl faklsdfalksdfasd\"\n}" - }, - "url": { - "raw": "{{ecordel_url}}/cordels", - "host": [ - "{{ecordel_url}}" - ], - "path": [ - "cordels" - ] - } - }, - "response": [ - { - "name": "Default", - "originalRequest": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}/cordels", - "host": [ - "{{url}}" - ], - "path": [ - "cordels" - ] - } - }, - "code": 200, - "_postman_previewlanguage": null, - "header": null, - "cookie": [], - "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" - } - ] - } - ] - }, - { - "name": "authors", - "item": [ - { - "name": "new author", - "request": { - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJlLWNvcmRlbCIsInN1YiI6IjEiLCJpYXQiOjE1OTgwNDQ0ODAsImV4cCI6MTU5ODEzMDg4MH0.ugCv2Z3QW8eE7kLtkz6xr7EuNJhCHZ2o0kigw7Dc5kU", - "type": "string" - } - ] - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"name\": \"Author Test\",\n \"about\": \"About author test\",\n \"email\": \"author.test@ecordel.com\"\n}" - }, - "url": { - "raw": "{{ecordel_url}}/authors", - "host": [ - "{{ecordel_url}}" - ], - "path": [ - "authors" - ] - } - }, - "response": [ - { - "name": "Default", - "originalRequest": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}/cordels", - "host": [ - "{{url}}" - ], - "path": [ - "cordels" - ] - } - }, - "code": 200, - "_postman_previewlanguage": null, - "header": null, - "cookie": [], - "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" - } - ] - }, - { - "name": "authors", - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{ecordel_url}}/authors", - "host": [ - "{{ecordel_url}}" - ], - "path": [ - "authors" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "auth", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "name": "Content-Type", - "type": "text", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n\t\"username\":\"admin\",\n\t\"password\":\"admin\"\n}" - }, - "url": { - "raw": "{{ecordel_url}}/auth", - "host": [ - "{{ecordel_url}}" - ], - "path": [ - "auth" - ] - } - }, - "response": [ - { - "name": "Default", - "originalRequest": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{url}}/cordels", - "host": [ - "{{url}}" - ], - "path": [ - "cordels" - ] - } - }, - "code": 200, - "_postman_previewlanguage": null, - "header": null, - "cookie": [], - "body": "[\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 4.3\n },\n {\n xilogravura : 'https://img.elo7.com.br/product/original/1EF2F6C/xilogravura-carro-de-boi-xilogravura.jpg',\n text: 'loren ipson dolor',\n author: 'Mário Focking Santos',\n title: 'My first e-cordel',\n rating: 3\n }\n ]" - } - ] - } - ] -} \ No newline at end of file