Skip to content

Commit

Permalink
Contracts commands cache implementation (#82)
Browse files Browse the repository at this point in the history
* working on added basic boilerplate

* formatting changes

* working on progress milestone command

* working on progress milestone command

* worked on implementing remaining contracts commands, added caching

* format message

* small fix to use send and receive instead of just send

* fixed small bug

* adding testing

* formating

* replaced every sys out with the logger template

---------

Co-authored-by: HusseinYasser <husseinyasser388@gmail.com>
  • Loading branch information
Kemosalamy and HusseinYasser authored May 12, 2024
1 parent 3e534ed commit fda9c13
Show file tree
Hide file tree
Showing 26 changed files with 613 additions and 23 deletions.
50 changes: 47 additions & 3 deletions services/contracts/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand All @@ -36,17 +40,57 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>cassandra</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>rabbitmq</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.redis.testcontainers</groupId>
<artifactId>testcontainers-redis-junit-jupiter</artifactId>
<version>1.4.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.workup")
@EnableCaching
public class ContractsApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
import com.workup.shared.commands.Command;
import com.workup.shared.commands.CommandRequest;
import com.workup.shared.commands.CommandResponse;
import com.workup.shared.redis.RedisService;
import lombok.Setter;
import org.springframework.amqp.core.AmqpTemplate;

public abstract class ContractCommand<T extends CommandRequest, Q extends CommandResponse>
implements Command<T, Q> {

@Setter AmqpTemplate rabbitTemplate;

@Setter ContractRepository contractRepository;

@Setter ContractMilestoneRepository contractMilestoneRepository;

@Setter TerminationRequestRepository terminationRequestRepository;

@Setter RedisService redisService;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.workup.shared.commands.CommandMap;
import com.workup.shared.commands.CommandRequest;
import com.workup.shared.commands.CommandResponse;
import com.workup.shared.redis.RedisService;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -19,6 +21,10 @@ public class ContractCommandMap

@Autowired TerminationRequestRepository terminationRequestRepository;

@Autowired AmqpTemplate rabbitTemplate;

@Autowired RedisService redisService;

public void registerCommands() {
commands.put("InitiateContract", InitiateContractCommand.class);
commands.put("RequestContractTermination", RequestContractTerminationCommand.class);
Expand All @@ -34,5 +40,7 @@ public void setupCommand(
command.setContractRepository(contractRepository);
command.setContractMilestoneRepository(contractMilestoneRepository);
command.setTerminationRequestRepository(terminationRequestRepository);
command.setRabbitTemplate(rabbitTemplate);
command.setRedisService(redisService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.workup.contracts.commands;

import com.workup.contracts.logger.ContractsLogger;
import com.workup.contracts.models.Contract;
import com.workup.contracts.models.ContractMilestone;
import com.workup.shared.commands.contracts.requests.EvaluateMilestoneRequest;
import com.workup.shared.commands.contracts.responses.EvaluateMilestoneResponse;
import com.workup.shared.commands.payments.paymentrequest.requests.CreatePaymentRequestRequest;
import com.workup.shared.enums.HttpStatusCode;
import com.workup.shared.enums.ServiceQueueNames;
import com.workup.shared.enums.contracts.MilestoneState;
import java.util.Optional;
import java.util.UUID;

public class EvaluateMilestoneCommand
extends ContractCommand<EvaluateMilestoneRequest, EvaluateMilestoneResponse> {
private EvaluateMilestoneResponse isValid(Optional<ContractMilestone> milestoneOptional) {
if (milestoneOptional.isEmpty())
return EvaluateMilestoneResponse.builder()
.withStatusCode(HttpStatusCode.BAD_REQUEST)
.withErrorMessage("Milestone is not found")
.build();
if (milestoneOptional.get().getStatus() != MilestoneState.IN_REVIEW)
return EvaluateMilestoneResponse.builder()
.withStatusCode(HttpStatusCode.BAD_REQUEST)
.withErrorMessage("Milestone cannot be evaluated as it has not progressed enough")
.build();
return null;
}

@Override
public EvaluateMilestoneResponse Run(EvaluateMilestoneRequest request) {
Optional<ContractMilestone> milestoneOptional =
contractMilestoneRepository.findById(UUID.fromString(request.getMilestoneId()));

EvaluateMilestoneResponse checkerResponse = isValid(milestoneOptional);
if (checkerResponse != null) return checkerResponse;

ContractMilestone updatedMilestone = milestoneOptional.get();

updatedMilestone.setStatus(request.getEvaluatedState());

try {
contractMilestoneRepository.save(updatedMilestone);
ContractsLogger.print(" [x] Milestone evaluated " + updatedMilestone);

if (request.getEvaluatedState() == MilestoneState.ACCEPTED) {
ContractsLogger.print(" [x] Sending payment request ");
// Getting the contract as we need to send the freelancer and client id since they are
// in the payment request parameters.
Optional<Contract> contractOptional =
contractRepository.findById(UUID.fromString(updatedMilestone.getContractId()));
if (contractOptional.isEmpty())
throw new Exception("Contract Optional was empty, therefore unable to fetch data");

Contract milestoneContract = contractOptional.get();

CreatePaymentRequestRequest externalRequest =
CreatePaymentRequestRequest.builder()
.withAmount(updatedMilestone.getAmount())
.withClientId(milestoneContract.getClientId())
.withFreelancerId(milestoneContract.getFreelancerId())
.withDescription(updatedMilestone.getMilestoneId().toString())
.build();
rabbitTemplate.convertSendAndReceive(ServiceQueueNames.PAYMENTS, externalRequest);
ContractsLogger.print(" [x] Payment request sent ");
}

return EvaluateMilestoneResponse.builder()
.withStatusCode(HttpStatusCode.OK)
.withErrorMessage("")
.build();

} catch (Exception e) {
e.printStackTrace();
return EvaluateMilestoneResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.withErrorMessage(e.getMessage())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.workup.contracts.commands;

import com.workup.contracts.logger.ContractsLogger;
import com.workup.contracts.models.Contract;
import com.workup.shared.commands.contracts.requests.GetContractRequest;
import com.workup.shared.commands.contracts.responses.GetContractResponse;
import com.workup.shared.enums.HttpStatusCode;
import java.util.Optional;
import java.util.UUID;

public class GetContractCommand extends ContractCommand<GetContractRequest, GetContractResponse> {

@Override
public GetContractResponse Run(GetContractRequest request) {
String cachingKey = request.getContractId();
GetContractResponse cachedResponse =
(GetContractResponse) redisService.getValue(cachingKey, GetContractResponse.class);
if (cachedResponse != null) {
ContractsLogger.print(
"[x] Contract request response fetched from cache: " + cachedResponse.toString());

return cachedResponse;
}

Optional<Contract> contractOptional =
contractRepository.findById(UUID.fromString(request.getContractId()));

if (contractOptional.isEmpty()) {
return GetContractResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Requested contract does not exist")
.build();
}

Contract contract = contractOptional.get();

GetContractResponse response =
GetContractResponse.builder()
.withContractId(contract.getContractId().toString())
.withProposalId(contract.getProposalId())
.withJobId(contract.getJobId())
.withJobTitle(contract.getJobTitle())
.withClientId(contract.getClientId())
.withFreelancerId(contract.getFreelancerId())
.withMilestonesIds(contract.getMilestonesIds())
.withStatus(contract.getStatus())
.withStatusCode(HttpStatusCode.OK)
.withErrorMessage("")
.build();

redisService.setValue(cachingKey, response);
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.workup.contracts.commands;

import com.workup.contracts.logger.ContractsLogger;
import com.workup.contracts.models.ContractMilestone;
import com.workup.shared.commands.contracts.requests.GetMilestoneRequest;
import com.workup.shared.commands.contracts.responses.GetMilestoneResponse;
import com.workup.shared.enums.HttpStatusCode;
import java.util.Optional;
import java.util.UUID;

public class GetMilestoneCommand
extends ContractCommand<GetMilestoneRequest, GetMilestoneResponse> {

@Override
public GetMilestoneResponse Run(GetMilestoneRequest request) {
String cachingKey = request.getMilestoneId();

GetMilestoneResponse cachedResponse =
(GetMilestoneResponse) redisService.getValue(cachingKey, GetMilestoneResponse.class);
if (cachedResponse != null) {
ContractsLogger.print(
"[x] Milestone request response fetched from cache: " + cachedResponse.toString());

return cachedResponse;
}

Optional<ContractMilestone> milestoneOptional =
contractMilestoneRepository.findById(UUID.fromString(request.getMilestoneId()));

if (milestoneOptional.isEmpty()) {
return GetMilestoneResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Requested milestone does not exist")
.build();
}

ContractMilestone milestone = milestoneOptional.get();

GetMilestoneResponse response =
GetMilestoneResponse.builder()
.withContractId(milestone.getContractId())
.withMilestoneId(milestone.getMilestoneId().toString())
.withAmount(milestone.getAmount())
.withDescription(milestone.getDescription())
.withDueDate(milestone.getDueDate())
.withAmount(milestone.getAmount())
.withStatusCode(HttpStatusCode.OK)
.withErrorMessage("")
.build();

redisService.setValue(cachingKey, response);
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.workup.contracts.commands;

import com.workup.contracts.logger.ContractsLogger;
import com.workup.contracts.models.TerminationRequest;
import com.workup.shared.commands.contracts.requests.GetPendingTerminationsRequest;
import com.workup.shared.commands.contracts.responses.GetPendingTerminationsResponse;
import com.workup.shared.enums.HttpStatusCode;
import java.util.List;

public class GetPendingTerminationsCommand
extends ContractCommand<GetPendingTerminationsRequest, GetPendingTerminationsResponse> {

@Override
public GetPendingTerminationsResponse Run(GetPendingTerminationsRequest request) {
String cachingKey = request.getContractId() + "/pending_terminations";

GetPendingTerminationsResponse cachedResponse =
(GetPendingTerminationsResponse)
redisService.getValue(cachingKey, GetPendingTerminationsResponse.class);
if (cachedResponse != null) {
ContractsLogger.print(
"[x] Contract terminations response fetched from cache: " + cachedResponse.toString());

return cachedResponse;
}

List<TerminationRequest> terminationsList =
terminationRequestRepository.findByContractId(request.getContractId());

if (terminationsList.isEmpty()) {
return GetPendingTerminationsResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("No pending terminations exist")
.build();
}

TerminationRequest terminationRequest = terminationsList.getFirst();

GetPendingTerminationsResponse response =
GetPendingTerminationsResponse.builder()
.withRequestId(terminationRequest.getRequestId().toString())
.withRequesterId(terminationRequest.getRequesterId())
.withContractId(terminationRequest.getContractId())
.withReason(terminationRequest.getReason())
.withStatus(terminationRequest.getStatus())
.withStatusCode(HttpStatusCode.OK)
.withErrorMessage("")
.build();

redisService.setValue(cachingKey, response);
return response;
}
}
Loading

0 comments on commit fda9c13

Please sign in to comment.