diff --git a/services/jobs/.env b/services/jobs/.env index 59921715..621ca11a 100644 --- a/services/jobs/.env +++ b/services/jobs/.env @@ -1,2 +1,3 @@ JOBS_DB_URL=jobs_db:9042 JOBS_MQ_URL=service_mq +REDIS_URL=service_redis \ No newline at end of file diff --git a/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java b/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java index 4ae506ef..39d65e0f 100644 --- a/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java +++ b/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java @@ -9,12 +9,14 @@ 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.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @SpringBootApplication @EnableAsync +@EnableCaching public class JobsApplication { public static void main(String[] args) { diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/AcceptProposalCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/AcceptProposalCommand.java index 8e291a36..f4117948 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/AcceptProposalCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/AcceptProposalCommand.java @@ -73,9 +73,10 @@ public AcceptProposalResponse Run(AcceptProposalRequest request) { .build(); } acceptedJob.setActive(false); + acceptedJob.setSearchIndex(null); + String contractId = initiateContract(acceptedProposal, acceptedJob, request); proposalRepository.save(acceptedProposal); jobRepository.save(acceptedJob); - String contractId = initiateContract(acceptedProposal, acceptedJob, request); logger.info("[x] Contract Initiated with id" + contractId); return AcceptProposalResponse.builder() .withStatusCode(HttpStatusCode.OK) @@ -89,7 +90,7 @@ public AcceptProposalResponse Run(AcceptProposalRequest request) { .withContractId(contractId) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while accepting job proposal", e.getMessage()); + logger.error("[x] An error occurred while accepting job proposal" + e.getMessage()); return AcceptProposalResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while accepting job proposal") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/CreateJobCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/CreateJobCommand.java index 6e654bc7..b28a83f9 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/CreateJobCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/CreateJobCommand.java @@ -37,7 +37,7 @@ public CreateJobResponse Run(CreateJobRequest request) { .withJobId(savedJob.getId().toString()) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while saving job", e.getMessage()); + logger.error("[x] An error occurred while saving job" + e.getMessage()); return CreateJobResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while saving job") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/CreateProposalCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/CreateProposalCommand.java index a650a4ad..4bf0cdfa 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/CreateProposalCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/CreateProposalCommand.java @@ -76,7 +76,7 @@ public CreateProposalResponse Run(CreateProposalRequest request) { .withId(savedProposal.getPrimaryKey().getId().toString()) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while saving proposal", e.getMessage()); + logger.error("[x] An error occurred while saving proposal" + e.getMessage()); return CreateProposalResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while saving proposal") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/GetJobByIdCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/GetJobByIdCommand.java index cc2d9fb2..111335f3 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/GetJobByIdCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/GetJobByIdCommand.java @@ -29,7 +29,7 @@ public GetJobByIdResponse Run(GetJobByIdRequest request) { .withSkills(job.get().getSkills()) .withExperience(job.get().getExperienceLevel()) .withClientId(job.get().getClientId()) - .withIsActive(job.get().isActive()) + .withActive(job.get().isActive()) .withBudget(job.get().getBudget()) .withCreatedAt(job.get().getCreatedAt()) .withModifiedAt(job.get().getUpdatedAt()) @@ -43,7 +43,7 @@ public GetJobByIdResponse Run(GetJobByIdRequest request) { .build(); } } catch (Exception e) { - logger.error("[x] An error occurred while fetching job", e.getMessage()); + logger.error("[x] An error occurred while fetching job" + e.getMessage()); return GetJobByIdResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/GetMyJobsCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/GetMyJobsCommand.java index 842834b0..f4ec3b0c 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/GetMyJobsCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/GetMyJobsCommand.java @@ -35,7 +35,7 @@ public GetMyJobsResponse Run(GetMyJobsRequest request) { .withStatusCode(HttpStatusCode.OK) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while fetching jobs", e.getMessage()); + logger.error("[x] An error occurred while fetching jobs" + e.getMessage()); return GetMyJobsResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while fetching jobs") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/GetMyProposalsCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/GetMyProposalsCommand.java index 4f4bcc1b..575b13ae 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/GetMyProposalsCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/GetMyProposalsCommand.java @@ -69,7 +69,7 @@ public GetMyProposalsResponse Run(GetMyProposalsRequest request) { .withStatusCode(HttpStatusCode.OK) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while fetching proposals", e.getMessage()); + logger.error("[x] An error occurred while fetching proposals" + e.getMessage()); return GetMyProposalsResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while fetching proposals") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/GetProposalsByJobIdCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/GetProposalsByJobIdCommand.java index d6bc5095..ff58735b 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/GetProposalsByJobIdCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/GetProposalsByJobIdCommand.java @@ -67,7 +67,7 @@ public GetProposalsByJobIdResponse Run(GetProposalsByJobIdRequest request) { .withStatusCode(HttpStatusCode.OK) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while fetching proposals", e.getMessage()); + logger.error("[x] An error occurred while fetching proposals" + e.getMessage()); return GetProposalsByJobIdResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage("An error occurred while fetching proposals") diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/SearchJobsCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/SearchJobsCommand.java index ecb6b361..1d70ff6d 100644 --- a/services/jobs/src/main/java/com/workup/jobs/commands/SearchJobsCommand.java +++ b/services/jobs/src/main/java/com/workup/jobs/commands/SearchJobsCommand.java @@ -53,7 +53,7 @@ public SearchJobsResponse Run(SearchJobsRequest request) { .withStatusCode(HttpStatusCode.OK) .build(); } catch (Exception e) { - logger.error("[x] An error occurred while searching for jobs", e.getMessage()); + logger.error("[x] An error occurred while searching for jobs" + e.getMessage()); return SearchJobsResponse.builder() .withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR) .withErrorMessage(e.getMessage()) diff --git a/services/jobs/src/main/java/com/workup/jobs/config/RedisConfig.java b/services/jobs/src/main/java/com/workup/jobs/config/RedisConfig.java new file mode 100644 index 00000000..cc85e363 --- /dev/null +++ b/services/jobs/src/main/java/com/workup/jobs/config/RedisConfig.java @@ -0,0 +1,8 @@ +package com.workup.jobs.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import(com.workup.shared.redis.RedisConfig.class) +public class RedisConfig {} diff --git a/services/jobs/src/main/java/com/workup/jobs/models/Attachment.java b/services/jobs/src/main/java/com/workup/jobs/models/Attachment.java index a77a8eea..06547453 100644 --- a/services/jobs/src/main/java/com/workup/jobs/models/Attachment.java +++ b/services/jobs/src/main/java/com/workup/jobs/models/Attachment.java @@ -1,5 +1,6 @@ package com.workup.jobs.models; +import java.io.Serializable; import lombok.Builder; import lombok.Getter; import org.springframework.data.cassandra.core.mapping.UserDefinedType; @@ -7,7 +8,7 @@ @Getter @Builder(setterPrefix = "with") @UserDefinedType -public class Attachment { +public class Attachment implements Serializable { private String url; private String name; diff --git a/services/jobs/src/main/java/com/workup/jobs/models/Job.java b/services/jobs/src/main/java/com/workup/jobs/models/Job.java index 61bc0790..2da7bd8f 100644 --- a/services/jobs/src/main/java/com/workup/jobs/models/Job.java +++ b/services/jobs/src/main/java/com/workup/jobs/models/Job.java @@ -1,6 +1,7 @@ package com.workup.jobs.models; import com.workup.shared.enums.jobs.Experience; +import java.io.Serializable; import java.util.Date; import java.util.UUID; import lombok.Builder; @@ -17,7 +18,7 @@ @Getter @Builder(setterPrefix = "with") @Table("jobs") -public class Job { +public class Job implements Serializable { @PrimaryKey private UUID id; @@ -28,6 +29,7 @@ public class Job { @SASI(indexMode = IndexMode.CONTAINS) @Column("search_index") + @Setter private String searchIndex; @CassandraType(type = CassandraType.Name.LIST, typeArguments = CassandraType.Name.TEXT) diff --git a/services/jobs/src/main/java/com/workup/jobs/models/Milestone.java b/services/jobs/src/main/java/com/workup/jobs/models/Milestone.java index 15097abb..845608ac 100644 --- a/services/jobs/src/main/java/com/workup/jobs/models/Milestone.java +++ b/services/jobs/src/main/java/com/workup/jobs/models/Milestone.java @@ -1,5 +1,6 @@ package com.workup.jobs.models; +import java.io.Serializable; import java.util.Date; import lombok.Builder; import lombok.Getter; @@ -8,7 +9,7 @@ @Getter @Builder(setterPrefix = "with") @UserDefinedType -public class Milestone { +public class Milestone implements Serializable { private String description; private Date dueDate; diff --git a/services/jobs/src/main/java/com/workup/jobs/models/Proposal.java b/services/jobs/src/main/java/com/workup/jobs/models/Proposal.java index 331561b1..926d39cd 100644 --- a/services/jobs/src/main/java/com/workup/jobs/models/Proposal.java +++ b/services/jobs/src/main/java/com/workup/jobs/models/Proposal.java @@ -2,6 +2,7 @@ import com.workup.shared.commands.jobs.proposals.JobDuration; import com.workup.shared.commands.jobs.proposals.ProposalStatus; +import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.UUID; @@ -20,7 +21,7 @@ @Getter @Builder(setterPrefix = "with") @Table("proposals") -public class Proposal { +public class Proposal implements Serializable { @PrimaryKey private Proposal.ProposalPrimaryKey primaryKey; @@ -51,7 +52,7 @@ public class Proposal { @PrimaryKeyClass @Builder(setterPrefix = "with") @Getter - public static class ProposalPrimaryKey { + public static class ProposalPrimaryKey implements Serializable { @PrimaryKeyColumn(name = "job_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) private String jobId; diff --git a/services/jobs/src/main/java/com/workup/jobs/repositories/JobRepository.java b/services/jobs/src/main/java/com/workup/jobs/repositories/JobRepository.java index 076eace0..40a0b5cd 100644 --- a/services/jobs/src/main/java/com/workup/jobs/repositories/JobRepository.java +++ b/services/jobs/src/main/java/com/workup/jobs/repositories/JobRepository.java @@ -2,7 +2,10 @@ import com.workup.jobs.models.Job; import java.util.List; +import java.util.Optional; import java.util.UUID; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.data.cassandra.repository.Query; import org.springframework.data.domain.Pageable; @@ -14,4 +17,10 @@ public interface JobRepository extends CassandraRepository { @Query("SELECT * FROM jobs_data.jobs WHERE client_id = ?0") public List getJobsByClientId(String clientId); + + @Cacheable(value = "jobs", key = "#jobId") + public Optional findById(UUID jobId); + + @CacheEvict(value = "jobs", key = "#entity.id") + S save(S entity); } diff --git a/services/jobs/src/main/java/com/workup/jobs/repositories/ProposalRepository.java b/services/jobs/src/main/java/com/workup/jobs/repositories/ProposalRepository.java index 98fd3705..9bcb7f90 100644 --- a/services/jobs/src/main/java/com/workup/jobs/repositories/ProposalRepository.java +++ b/services/jobs/src/main/java/com/workup/jobs/repositories/ProposalRepository.java @@ -2,14 +2,20 @@ import com.workup.jobs.models.Proposal; import java.util.List; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.cassandra.repository.CassandraRepository; import org.springframework.data.cassandra.repository.Query; public interface ProposalRepository extends CassandraRepository { @Query("SELECT * FROM jobs_data.proposals WHERE job_id = ?0") + @Cacheable(value = "proposals", key = "#jobId") public List findByJobId(String jobId); @Query("SELECT * FROM jobs_data.proposals WHERE freelancer_id = ?0") public List findByFreelancerId(String freelancerId); + + @CacheEvict(value = "proposals", key = "#entity.primaryKey.jobId") + S save(S entity); } diff --git a/services/jobs/src/main/resources/application.properties b/services/jobs/src/main/resources/application.properties index ce5d1578..758a54d6 100644 --- a/services/jobs/src/main/resources/application.properties +++ b/services/jobs/src/main/resources/application.properties @@ -6,3 +6,8 @@ spring.cassandra.local-datacenter=datacenter1 spring.cassandra.keyspace-name=jobs_data spring.cassandra.contact-points=${JOBS_DB_URL} spring.cassandra.schema-action=CREATE_IF_NOT_EXISTS + +spring.cache.type=redis +spring.cache.host=${REDIS_URL} +spring.cache.port=6379 +spring.cache.redis.time-to-live=60000 \ No newline at end of file diff --git a/services/jobs/src/test/java/com/workup/jobs/JobsApplicationTests.java b/services/jobs/src/test/java/com/workup/jobs/JobsApplicationTests.java index aa3879ba..979d2cd6 100644 --- a/services/jobs/src/test/java/com/workup/jobs/JobsApplicationTests.java +++ b/services/jobs/src/test/java/com/workup/jobs/JobsApplicationTests.java @@ -16,8 +16,10 @@ import com.workup.shared.commands.jobs.proposals.responses.AcceptProposalResponse; import com.workup.shared.commands.jobs.proposals.responses.CreateProposalResponse; import com.workup.shared.commands.jobs.requests.CreateJobRequest; +import com.workup.shared.commands.jobs.requests.GetJobByIdRequest; import com.workup.shared.commands.jobs.requests.SearchJobsRequest; import com.workup.shared.commands.jobs.responses.CreateJobResponse; +import com.workup.shared.commands.jobs.responses.GetJobByIdResponse; import com.workup.shared.commands.jobs.responses.SearchJobsResponse; import com.workup.shared.enums.HttpStatusCode; import com.workup.shared.enums.ServiceQueueNames; @@ -36,6 +38,7 @@ import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.CassandraContainer; +import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -56,6 +59,10 @@ static String GetCassandraContactPoint() { return cassandraContainer.getHost() + ":" + cassandraContainer.getFirstMappedPort(); } + @Container + static final GenericContainer redisContainer = + new GenericContainer("redis:7.2.4").withExposedPorts(6379); + @DynamicPropertySource static void datasourceProperties(DynamicPropertyRegistry registry) { registry.add("spring.cassandra.contact-points", JobsApplicationTests::GetCassandraContactPoint); @@ -64,6 +71,8 @@ static void datasourceProperties(DynamicPropertyRegistry registry) { registry.add("spring.rabbitmq.port", rabbitMQContainer::getFirstMappedPort); registry.add("spring.rabbitmq.username", rabbitMQContainer::getAdminUsername); registry.add("spring.rabbitmq.password", rabbitMQContainer::getAdminPassword); + registry.add("spring.cache.host", redisContainer::getHost); + registry.add("spring.cache.port", redisContainer::getFirstMappedPort); } private static final String CLIENT_ONE_ID = "123"; @@ -106,6 +115,36 @@ void testCreateJob() { () -> new RuntimeException("Job not found")); } + @Test + void testGetJobById() { + Job job = + jobRepository.save( + Job.builder() + .withId(UUID.randomUUID()) + .withTitle("Convert HTML Template to React 3") + .withDescription( + "I have an HTML template that I have purchased and own the rights to. I would" + + " like it converted into a React application.") + .withSkills(new String[] {"HTML", "CSS", "JavaScript", "React"}) + .withClientId(CLIENT_ONE_ID) + .withIsActive(true) + .build()); + + GetJobByIdResponse response = + (GetJobByIdResponse) + template.convertSendAndReceive( + ServiceQueueNames.JOBS, + GetJobByIdRequest.builder().withJobId(job.getId().toString()).build()); + + assertNotNull(response); + assertTrue(response.getStatusCode() == HttpStatusCode.OK); + assertEquals(job.getId().toString(), response.getId()); + assertEquals(job.getTitle(), response.getTitle()); + assertEquals(job.getDescription(), response.getDescription()); + assertEquals(job.getClientId(), response.getClientId()); + assertEquals(true, response.isActive()); + } + /** * Creates a proposal for a given a job ID. * diff --git a/shared/src/main/java/com/workup/shared/commands/jobs/responses/GetJobByIdResponse.java b/shared/src/main/java/com/workup/shared/commands/jobs/responses/GetJobByIdResponse.java index 8b62a622..f25ece09 100644 --- a/shared/src/main/java/com/workup/shared/commands/jobs/responses/GetJobByIdResponse.java +++ b/shared/src/main/java/com/workup/shared/commands/jobs/responses/GetJobByIdResponse.java @@ -20,7 +20,7 @@ public class GetJobByIdResponse extends CommandResponse { private final String[] skills; private final Experience experience; private final String clientId; - private final boolean isActive; + private final boolean active; private final Date createdAt; private final Date modifiedAt; }