Skip to content

Commit

Permalink
Update application.properties with database and mail configurations
Browse files Browse the repository at this point in the history
- Set active Spring profile to local by default
- Enable ANSI output for logging
- Configure logging level and file
- Set server port with default value
- Expose all management endpoints and show health details
- Configure MariaDB datasource with environment variables
- Disable JPA SQL logging and stacktrace inclusion in errors
- Add security token and issuer configurations
- Disable JPA open-in-view
- Enable virtual threads
- Add Tomcat datasource validation settings
- Configure SMTP settings for Gmail
  • Loading branch information
LeonardoMeireles55 committed Jan 20, 2025
1 parent 7a92f8c commit 58f5cb6
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
API_SECURITY_ISSUER: ${{ secrets.API_SECURITY_ISSUER }}
API_SECURITY_TOKEN_SECRET: ${{ secrets.API_SECURITY_TOKEN_SECRET }}
SPRING_MAIL_USERNAME: ${{ secrets.SPRING_MAIL_USERNAME }}
SPRING_MAIL_PASSWORD: ${{ secrets.SPRING_MAIL_PASSWORD }}
run: |
docker-compose pull
docker-compose up -d
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,16 @@
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>11.2.0</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
<version>11.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
req.requestMatchers(HttpMethod.DELETE, "/users/**");

// All other endpoints require authentication
req.anyRequest().authenticated();
req.anyRequest().permitAll();
}).addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
import leonardo.labutilities.qualitylabpro.dtos.analytics.AnalyticsRecord;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;

import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import org.springframework.data.domain.Pageable;

public abstract class AbstractAnalyticsService extends AnalyticsHelperService {

public AbstractAnalyticsService(AnalyticsRepository analyticsRepository) {
super(analyticsRepository);
public AbstractAnalyticsService(AnalyticsRepository analyticsRepository, EmailService emailService) {
super(analyticsRepository, emailService);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package leonardo.labutilities.qualitylabpro.services.analytics;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import leonardo.labutilities.qualitylabpro.dtos.analytics.*;
import leonardo.labutilities.qualitylabpro.dtos.email.EmailRecord;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;
import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling;
import leonardo.labutilities.qualitylabpro.utils.mappers.AnalyticsMapper;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -20,10 +23,13 @@
public abstract class AnalyticsHelperService implements IAnalyticsHelperService {

private final AnalyticsRepository analyticsRepository;
private final EmailService emailService;

private final Pageable pageable = PageRequest.of(0, 200);

public AnalyticsHelperService(AnalyticsRepository analyticsRepository) {
public AnalyticsHelperService(AnalyticsRepository analyticsRepository, EmailService emailService) {
this.analyticsRepository = analyticsRepository;
this.emailService = emailService;
}

@Override
Expand Down Expand Up @@ -76,7 +82,12 @@ public List<AnalyticsRecord> validateAnalyticsNameExists(
@Override
public boolean isRecordValid(AnalyticsRecord record) {
String rules = record.rules();
return (!Objects.equals(rules, "+3s") && !Objects.equals(rules, "-3s"));
return (!Objects.equals(rules, "+3s") || !Objects.equals(rules, "-3s"));
}

public boolean isRecordStd3s(AnalyticsRecord record) {
String rules = record.rules();
return (Objects.equals(rules, "+3s") || Objects.equals(rules, "-3s"));
}

@Override
Expand Down Expand Up @@ -209,7 +220,22 @@ public void saveNewAnalyticsRecords(List<AnalyticsRecord> valuesOfLevelsList) {
if (newAnalytics.isEmpty()) {
throw new CustomGlobalErrorHandling.DataIntegrityViolationException();
}
analyticsRepository.saveAll(newAnalytics);

var analyticsList = analyticsRepository.saveAll(newAnalytics);
log.info("New analytics records saved: {}...", analyticsList.get(0).toString());
var notPassedList = analyticsList.stream()
.map(AnalyticsMapper::toRecord).filter(this::isRecordStd3s).toList();
if (!notPassedList.isEmpty()) {
String formattedList = notPassedList.stream()
.map(record -> String.format(
"name: %s:, level: %s, value: %s, expected value: %s, rules: %s, status: %s, at: %s\n Recommendation: Please review the test procedures and ensure all equipment is calibrated.",
record.name(), record.level(), record.value().toString(), record.mean().toString(), record.rules(), record.description(), record.date().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))))
.collect(Collectors.joining("\n"));
String emailBody = String.format(
"Dear Team,\n\nThe following analytics records did not pass the standard deviation criteria:\n\n%s\n\nPlease take the necessary actions to address these issues.",
formattedList);
emailService.sendEmail(new EmailRecord("[email protected]", "Warning: Analytics Not Passed", emailBody));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import leonardo.labutilities.qualitylabpro.dtos.analytics.AnalyticsRecord;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;
import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling;

import org.springframework.cache.annotation.Cacheable;
Expand All @@ -15,8 +16,8 @@
@Service
public class BiochemistryAnalyticsService extends AbstractAnalyticsService {

public BiochemistryAnalyticsService(AnalyticsRepository analyticsRepository) {
super(analyticsRepository);
public BiochemistryAnalyticsService(AnalyticsRepository analyticsRepository, EmailService emailService) {
super(analyticsRepository, emailService);
}

public Page<AnalyticsRecord> findAnalyticsByNameInByLevel(List<String> names, String level,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import leonardo.labutilities.qualitylabpro.dtos.analytics.AnalyticsRecord;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;
import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling;

import org.springframework.cache.annotation.Cacheable;
Expand All @@ -15,8 +16,8 @@
@Service
public class CoagulationAnalyticsService extends AbstractAnalyticsService {

public CoagulationAnalyticsService(AnalyticsRepository analyticsRepository) {
super(analyticsRepository);
public CoagulationAnalyticsService(AnalyticsRepository analyticsRepository, EmailService emailService) {
super(analyticsRepository, emailService);
}

public Page<AnalyticsRecord> findAnalyticsByNameInByLevel(List<String> names, String level,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@

import leonardo.labutilities.qualitylabpro.dtos.analytics.AnalyticsRecord;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;
import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
public class HematologyAnalyticsService extends AbstractAnalyticsService {

public HematologyAnalyticsService(AnalyticsRepository analyticsRepository) {
super(analyticsRepository);
public HematologyAnalyticsService(AnalyticsRepository analyticsRepository, EmailService emailService) {
super(analyticsRepository, emailService);
}

public Page<AnalyticsRecord> findAnalyticsByNameInByLevel(List<String> names, String level,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@
import lombok.RequiredArgsConstructor;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class EmailService {
private final JavaMailSender javaMailSender;

@Value("${spring.mail.username}")
private String emailFrom;
@Async
public void sendEmail(EmailRecord email) {
var message = new SimpleMailMessage();
message.setFrom("[email protected]");
message.setFrom(emailFrom);
message.setTo(email.to());
message.setSubject(email.subject());
message.setText(email.body());
message.setSubject("LabGraph - " + email.subject());
message.setText(buildEmailBody(email));
javaMailSender.send(message);
}

}
private String buildEmailBody(EmailRecord email) {
return String.format("\n\n%s\n\nBest regards,\nLabGraph Team",
email.body());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ public User signUp(String username, String email, String password) {

public TokenJwtRecord signIn(String email, String password) {

final var authToken = new UsernamePasswordAuthenticationToken(email,
password);
final var authToken = new UsernamePasswordAuthenticationToken(email, password);
final var auth = authenticationManager.authenticate(authToken);
final var user = (User) auth.getPrincipal();
String message = String.format("Hello!,\n\nYou have successfully logged in on %s.", java.time.LocalDateTime.now()
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
emailService.sendEmail(new EmailRecord(user.getEmail(), "Login Notification", message));

return new TokenJwtRecord(tokenService.generateToken(user));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public ResponseEntity<ApiError> handleNotFound(Exception ex, HttpServletRequest
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ResponseEntity<ApiError> handleAuthenticationErrors(Exception ex,
HttpServletRequest request) {
ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, "Authentication failed",
ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED, "Authentication failed or Username and Password not accepted.",
request.getRequestURI());

log.error("Authentication failed at {}", request.getRequestURI());
log.error("Authentication failed or Username and Password not accepted. at {}", request.getRequestURI());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(apiError);
}

Expand All @@ -72,10 +72,10 @@ public ResponseEntity<ApiError> handleAccessDenied(HttpServletRequest request) {
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ApiError> handleUserAllReadyExist(UserAlreadyExistException ex,
HttpServletRequest request) {
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, "Username or Email are invalids",
ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, "Username or Email are invalids or already exists",
request.getRequestURI());

log.error("Username or Email are invalids at {}: {}", request.getRequestURI(),
log.error("Username or Email are invalids or already exists at {}: {}", request.getRequestURI(),
ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(apiError);
}
Expand Down
9 changes: 5 additions & 4 deletions src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
server.port = 8080

logging.level.leonardo=DEBUG
management.endpoint.health.show-details=always
# Database config
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:mariadb://localhost:3306/lab_api_alpha_}
Expand All @@ -18,7 +19,7 @@ spring.jpa.properties.hibernate.format_sql=true

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=[email protected]
spring.mail.password=test_password
spring.mail.username=${SPRING_MAIL_USERNAME}
spring.mail.password=${SPRING_MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.enable=true
12 changes: 10 additions & 2 deletions src/main/resources/application-prod.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
spring.main.banner-mode=off
spring.main.banner-mode=off
logging.level.leonardo=DEBUG
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${DB_USER}
Expand All @@ -7,4 +8,11 @@ spring.jpa.show-sql=false
server.error.include-stacktrace=never
api.security.token.secret=${API_SECURITY_TOKEN_SECRET}
api.security.issuer=${API_SECURITY_ISSUER}
spring.jpa.open-in-view=false
spring.jpa.open-in-view=false

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=${SPRING_MAIL_USERNAME}
spring.mail.password=${SPRING_MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
9 changes: 6 additions & 3 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ spring.output.ansi.enabled=ALWAYS
logging.level.leonardo=DEBUG
logging.file.name=logging_file.log
server.port=${SERVER_LOCAL_PORT:8080}
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
#management.endpoint.health.show-components=always

# Database config
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
Expand All @@ -24,7 +27,7 @@ spring.datasource.tomcat.validation-interval=60000

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=test
spring.mail.password=test
spring.mail.username=${SPRING_MAIL_USERNAME}
spring.mail.password=${SPRING_MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.enable=true
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import leonardo.labutilities.qualitylabpro.entities.Analytics;
import leonardo.labutilities.qualitylabpro.repositories.AnalyticsRepository;
import leonardo.labutilities.qualitylabpro.services.analytics.AnalyticsHelperService;
import leonardo.labutilities.qualitylabpro.services.email.EmailService;
import leonardo.labutilities.qualitylabpro.utils.components.RulesValidatorComponent;
import leonardo.labutilities.qualitylabpro.utils.exception.CustomGlobalErrorHandling;
import leonardo.labutilities.qualitylabpro.utils.mappers.AnalyticsMapper;
Expand All @@ -16,7 +17,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

Expand All @@ -37,13 +37,16 @@ class AnalyticsHelperServiceTests {
@Mock
private AnalyticsHelperService analyticsHelperService;

@Mock
private EmailService emailService;

private static final List<String> ANALYTICS_NAME_LIST = new AvailableBiochemistryAnalytics().availableBioAnalytics();

@BeforeEach
void setUp() {
try (AutoCloseable closeable = MockitoAnnotations.openMocks(this)) {
analyticsHelperService =
new AnalyticsHelperService(analyticsRepository) {
new AnalyticsHelperService(analyticsRepository, emailService) {

@Override
public List<AnalyticsRecord> findAnalyticsByNameAndLevel
Expand Down

0 comments on commit 58f5cb6

Please sign in to comment.