Skip to content

Commit

Permalink
(#13 WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
seyedali-dev committed May 18, 2024
1 parent 25da9dd commit 01cb890
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ build/

### Environment Variables ###
src/main/resources/.env.properties

### DB Data ###
db/redis/
20 changes: 20 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

<!-- Caching -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- DataBase -->
<dependency>
<groupId>com.h2database</groupId>
Expand Down Expand Up @@ -129,6 +135,20 @@
</excludes>
</configuration>
</plugin>

<!-- This is for redis key in `TimeEntryTrackingServiceImpl#startTrackingTime(Boolean, BigDecimal)`; cuz the key is generating null -->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-compiler-plugin</artifactId>-->
<!-- <version>3.11.0</version>-->
<!-- <configuration>-->
<!-- <source>${java.version}</source>-->
<!-- <target>${java.version}</target>-->
<!-- <compilerArgs>-->
<!-- <arg>-parameters</arg>-->
<!-- </compilerArgs>-->
<!-- </configuration>-->
<!-- </plugin>-->
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.seyed.ali.timeentryservice.config.cache;

import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;

@Configuration
@EnableCaching
public class RedisConfiguration {

/**
* Remember to configure the properties in application.yml or properties file.
*/
@Bean
@Primary
public RedisProperties properties() {
return new RedisProperties();
}

/**
* Lettuce is non-blocking, and Jedis is blocking.
*/
@Bean
public LettuceConnectionFactory lettuceConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(this.properties().getHost());
redisStandaloneConfiguration.setPort(this.properties().getPort());

return new LettuceConnectionFactory(redisStandaloneConfiguration);
}

@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

redisTemplate.setConnectionFactory(this.lettuceConnectionFactory());
redisTemplate.setEnableTransactionSupport(true);

redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new GenericToStringSerializer<>(Object.class));

return redisTemplate;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public ResponseEntity<Result> getTimeEntries() {
));
}

@GetMapping("/{userId}")
@GetMapping("/user/{userId}")
public ResponseEntity<Result> getUsersTimeEntry(@PathVariable String userId) {
return ResponseEntity.ok(new Result(
true,
Expand All @@ -52,6 +52,16 @@ public ResponseEntity<Result> getUsersTimeEntry(@PathVariable String userId) {
));
}

@GetMapping("/{timeEntryId}")
public ResponseEntity<Result> getSpecificTimeEntry(@PathVariable String timeEntryId) {
return ResponseEntity.ok(new Result(
true,
OK,
"Time entry: '" + timeEntryId + "'.",
this.timeEntryService.getTimeEntryById(timeEntryId)
));
}

@PostMapping
public ResponseEntity<Result> addTimeEntryManually(@Valid @RequestBody TimeEntryDTO timeEntryDTO) {
return ResponseEntity.status(CREATED).body(new Result(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,11 @@ public class TimeEntryTrackingController {

@PostMapping("/start")
public ResponseEntity<Result> startTrackingTimeEntry(@Valid @RequestBody TimeBillingDTO timeBillingDTO) {
boolean billable = false;
BigDecimal hourlyRate = BigDecimal.ZERO;

if (timeBillingDTO != null) {
billable = timeBillingDTO.billable();
hourlyRate = timeBillingDTO.hourlyRate();
}
return ResponseEntity.status(CREATED).body(new Result(
true,
CREATED,
"Time tracking started...",
this.timeEntryService.startTrackingTimeEntry(billable, hourlyRate)
this.timeEntryService.startTrackingTimeEntry(timeBillingDTO)
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import com.seyed.ali.timeentryservice.util.TimeParser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -44,6 +46,20 @@ public TimeEntryResponse getUsersTimeEntry(String userId) {
return this.timeEntryUtility.convertToTimeEntryResponse(timeEntry);
}

/**
* {@inheritDoc}
*/
@Override
// @Cacheable(
// cacheNames = "time-entry-cache",
// key = "#timeEntryId"
// )
public TimeEntryResponse getTimeEntryById(String timeEntryId) {
return this.timeEntryRepository.findById(timeEntryId)
.map(this.timeEntryUtility::convertToTimeEntryResponse)
.orElseThrow(()-> new ResourceNotFoundException("Time entry with ID: '" + timeEntryId +"' was not found."));
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import com.seyed.ali.timeentryservice.client.AuthenticationServiceClient;
import com.seyed.ali.timeentryservice.exceptions.OperationNotSupportedException;
import com.seyed.ali.timeentryservice.model.domain.TimeEntry;
import com.seyed.ali.timeentryservice.model.dto.TimeBillingDTO;
import com.seyed.ali.timeentryservice.model.dto.TimeEntryDTO;
import com.seyed.ali.timeentryservice.repository.TimeEntryRepository;
import com.seyed.ali.timeentryservice.service.interfaces.TimeEntryTrackingService;
import com.seyed.ali.timeentryservice.util.TimeEntryUtility;
import com.seyed.ali.timeentryservice.util.TimeParser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -33,7 +36,19 @@ public class TimeEntryTrackingServiceImpl implements TimeEntryTrackingService {
*/
@Override
@Transactional
public String startTrackingTimeEntry(boolean billable, BigDecimal hourlyRate) {
@CachePut(
cacheNames = "time-entry-cache",
key = "#result"
)
public String startTrackingTimeEntry(TimeBillingDTO timeBillingDTO) {
boolean billable = false;
BigDecimal hourlyRate = BigDecimal.ZERO;

if (timeBillingDTO != null) {
billable = timeBillingDTO.billable();
hourlyRate = timeBillingDTO.hourlyRate();
}

TimeEntry timeEntry = this.timeEntryUtility.createNewTimeEntry(billable, hourlyRate, this.authenticationServiceClient);
this.timeEntryRepository.save(timeEntry);
return timeEntry.getTimeEntryId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ public interface TimeEntryService {
*/
TimeEntryResponse getUsersTimeEntry(String userId);

/**
* Retrieves a specific time entry.
*
* @param timeEntryId The ID of the time entry.
* @return A TimeEntryResponse object representing the found time entry.
* @throws ResourceNotFoundException if the time entry is not found.
*/
TimeEntryResponse getTimeEntryById(String timeEntryId);

/**
* Adds a new time entry manually.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.seyed.ali.timeentryservice.service.interfaces;

import com.seyed.ali.timeentryservice.model.dto.TimeBillingDTO;
import com.seyed.ali.timeentryservice.model.dto.TimeEntryDTO;

import java.math.BigDecimal;

/**
* Interface for Time Entry tracking service operations.
*/
Expand All @@ -12,11 +11,10 @@ public interface TimeEntryTrackingService {
/**
* Starts tracking a new time entry.
*
* @param billable A boolean indicating whether the time entry is billable or not.
* @param hourlyRate The hourly rate for the time entry (if billable).
* @param timeBillingDTO A dto class with: A boolean indicating whether the time entry is billable or not & The hourly rate for the time entry (if billable).
* @return The ID of the created time entry.
*/
String startTrackingTimeEntry(boolean billable, BigDecimal hourlyRate);
String startTrackingTimeEntry(TimeBillingDTO timeBillingDTO);

/**
* Stops tracking an existing time entry.
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ spring:
jwt:
issuer-uri: http://${KEYCLOAK_SERVER_HOST:localhost}:8080/realms/DevVault-v2.0

--- # Redis
spring:
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
cache:
type: redis
cache-names:
- time-entry-cache
redis:
cache-null-values: true
time-to-live: ${REDIS_CACHE_TIME_TO_LIVE:600000} # 1 hour

--- # Swagger
springdoc:
swagger-ui:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public void getUsersTimeEntryTest() throws Exception {

// When
ResultActions resultActions = this.mockMvc.perform(
MockMvcRequestBuilders.get(this.baseUrl + "/" + userId)
MockMvcRequestBuilders.get(this.baseUrl + "/user/" + userId)
.accept(APPLICATION_JSON)
.with(jwt().authorities(new SimpleGrantedAuthority(some_authority)))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void setUp() {
public void startTrackingTimeEntryTest() throws Exception {
// given
String timeEntryId = "some_time_entry_id";
when(this.timeEntryTrackingService.startTrackingTimeEntry(isA(Boolean.class), isA(BigDecimal.class)))
when(this.timeEntryTrackingService.startTrackingTimeEntry(isA(TimeBillingDTO.class)))
.thenReturn(timeEntryId);
String json = this.objectMapper.writeValueAsString(new TimeBillingDTO(true, BigDecimal.ONE));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.seyed.ali.timeentryservice.client.AuthenticationServiceClient;
import com.seyed.ali.timeentryservice.model.domain.TimeEntry;
import com.seyed.ali.timeentryservice.model.domain.TimeSegment;
import com.seyed.ali.timeentryservice.model.dto.TimeBillingDTO;
import com.seyed.ali.timeentryservice.model.dto.TimeEntryDTO;
import com.seyed.ali.timeentryservice.repository.TimeEntryRepository;
import com.seyed.ali.timeentryservice.util.TimeEntryUtility;
Expand Down Expand Up @@ -84,7 +85,8 @@ public void startTrackingTimeEntryTest() {
when(this.timeEntryRepository.save(isA(TimeEntry.class))).thenReturn(timeEntry);

// Act
String timeEntryId = this.timeEntryTrackingService.startTrackingTimeEntry(false, BigDecimal.ONE);
TimeBillingDTO timeBillingDTO = new TimeBillingDTO(false, BigDecimal.ZERO);
String timeEntryId = this.timeEntryTrackingService.startTrackingTimeEntry(timeBillingDTO);
System.out.println(timeEntryId);

// Assert
Expand Down

0 comments on commit 01cb890

Please sign in to comment.