Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RI-47: [job-board] change le statut d'une candidature lorsque la card est déplacée sur le job-board #197

Merged
merged 2 commits into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public JobApplicationDto toDto(JobApplication model) {
.jobTitle(job.getTitle())
.companyName(companyName)
.companyImageUri(companyImageUrl)
.location(job.getLocation())
.statusCode(model.getStatus().name())
.contractType(job.getContractType())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ public ResponseEntity<List<JobApplicationDto>> getAllAppliesByUserId() {
List<JobApplicationDto> applies = applyService.getAllAppliesForCurrentUser();
return ResponseEntity.ok(applies);
}

@PatchMapping
public ResponseEntity<JobApplicationDto> updateApply(@RequestBody JobApplicationRequest requestDto) {
final JobApplicationDto updateDto = applyService.updateApplyForCurrentUser(requestDto);
return ResponseEntity.ok(updateDto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class JobApplicationDto {

private String statusCode;

private String location;

private ContractTypeEnum contractType;

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.dynonuggets.refonteimplicaction.repository;

import com.dynonuggets.refonteimplicaction.model.JobApplication;
import com.dynonuggets.refonteimplicaction.model.JobPosting;
import com.dynonuggets.refonteimplicaction.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

Expand All @@ -11,7 +10,7 @@
// supprime les warnings sur les noms des méthodes ne respectant pas la convention de nommage (cf named queries)
@SuppressWarnings("squid:S00100")
public interface JobApplicationRepository extends JpaRepository<JobApplication, Long> {
Optional<JobApplication> findByJobAndUser_id(JobPosting job, long userId);
Optional<JobApplication> findByJob_IdAndUser_id(long jobId, long userId);

List<JobApplication> findAllByUserAndArchiveIsFalse(User user);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
import java.time.Instant;
import java.util.List;

import static com.dynonuggets.refonteimplicaction.utils.Message.APPLY_ALREADY_EXISTS_FOR_JOB;
import static com.dynonuggets.refonteimplicaction.utils.Message.JOB_NOT_FOUND_MESSAGE;
import static com.dynonuggets.refonteimplicaction.utils.Message.*;
import static java.util.stream.Collectors.toList;

@Service
Expand All @@ -29,14 +28,17 @@ public class JobApplicationService {
private final JobApplicationAdapter applyAdapter;
private final AuthService authService;

/**
* @return crée une candidature s'il l'utilisateur courant n'a pas déjà candidaté pour l'offre
*/
@Transactional
public JobApplicationDto createApplyIfNotExists(JobApplicationRequest applyRequest) {
final JobPosting job = jobRepository.findById(applyRequest.getJobId())
.orElseThrow(() -> new NotFoundException(String.format(JOB_NOT_FOUND_MESSAGE, applyRequest.getJobId())));

final User currentUser = authService.getCurrentUser();

if (applyRepository.findByJobAndUser_id(job, currentUser.getId()).isPresent()) {
if (applyRepository.findByJob_IdAndUser_id(job.getId(), currentUser.getId()).isPresent()) {
throw new IllegalArgumentException(String.format(APPLY_ALREADY_EXISTS_FOR_JOB, job.getId()));
}

Expand All @@ -53,6 +55,9 @@ public JobApplicationDto createApplyIfNotExists(JobApplicationRequest applyReque
return applyAdapter.toDto(applySave);
}

/**
* @return les candidatures non archivées de l'utilisateur courant
*/
@Transactional(readOnly = true)
public List<JobApplicationDto> getAllAppliesForCurrentUser() {
final User currentUser = authService.getCurrentUser();
Expand All @@ -62,4 +67,19 @@ public List<JobApplicationDto> getAllAppliesForCurrentUser() {
.map(applyAdapter::toDto)
.collect(toList());
}

@Transactional
public JobApplicationDto updateApplyForCurrentUser(JobApplicationRequest requestDto) {
final User currentUser = authService.getCurrentUser();
final Long jobId = requestDto.getJobId();
final Long currentUserId = currentUser.getId();

final JobApplication jobApplication = applyRepository.findByJob_IdAndUser_id(jobId, currentUserId)
.orElseThrow(() -> new NotFoundException(String.format(APPLY_NOT_FOUND_WITH_JOB_AND_USER, jobId, currentUserId)));

jobApplication.setStatus(requestDto.getStatus());
final JobApplication applySave = applyRepository.save(jobApplication);

return applyAdapter.toDto(applySave);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public JobPostingDto getJobById(Long jobId) {
final Long currentUserId = authService.getCurrentUser().getId();
final JobPostingDto jobDto = jobPostingAdapter.toDto(job);

jobDto.setApply(jobApplicationRepository.findByJobAndUser_id(job, currentUserId).isPresent());
jobDto.setApply(jobApplicationRepository.findByJob_IdAndUser_id(job.getId(), currentUserId).isPresent());

return jobDto;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class Message {

// JobApplication messages
public static final String APPLY_NOT_FOUND_MESSAGE = "No apply found with id [%d]";
public static final String APPLY_NOT_FOUND_WITH_JOB_AND_USER = "No apply found for user [%d] and jobId [%d]";
public static final String APPLY_ALREADY_EXISTS_FOR_JOB = "Unable to apply, apply already exists with jobId [%d]";

public static final String BAD_CREDENTIAL_MESSAGE = "Nom d'utilisateur ou mot de passe incorrect.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import static org.mockito.Mockito.*;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(controllers = JobApplicationController.class)
Expand All @@ -39,7 +39,7 @@ class JobApplicationControllerTest extends ControllerIntegrationTestBase {
void should_create_apply() throws Exception {
// given
JobApplicationRequest request = new JobApplicationRequest(123L, PENDING);
JobApplicationDto response = new JobApplicationDto(243L, 123L, "Mon super Job", "Google", "http://uri.com", PENDING.name(), CDI);
JobApplicationDto response = new JobApplicationDto(243L, 123L, "Mon super Job", "Google", "http://uri.com", PENDING.name(), "Paris (75)", CDI);
given(applicationService.createApplyIfNotExists(any())).willReturn(response);
String json = gson.toJson(request);

Expand Down Expand Up @@ -128,9 +128,9 @@ void should_return_forbidden_when_creating_apply_and_no_auth() throws Exception
void should_list_all_users_application() throws Exception {
// given
List<JobApplicationDto> expecteds = asList(
new JobApplicationDto(1L, 12L, "super job", "google", "http://url.com", PENDING.name(), CDD),
new JobApplicationDto(2L, 13L, "super job 2", "microsof", "http://url2.com", CHASED.name(), CDD),
new JobApplicationDto(3L, 14L, "super job 3", "amazon", "http://url3.com", INTERVIEW.name(), INTERIM)
new JobApplicationDto(1L, 12L, "super job", "google", "http://url.com", PENDING.name(), "Paris (75)", CDD),
new JobApplicationDto(2L, 13L, "super job 2", "microsof", "http://url2.com", CHASED.name(), "Paris (75)", CDD),
new JobApplicationDto(3L, 14L, "super job 3", "amazon", "http://url3.com", INTERVIEW.name(), "Paris (75)", INTERIM)
);
int expectedSize = expecteds.size();
given(applicationService.getAllAppliesForCurrentUser()).willReturn(expecteds);
Expand Down Expand Up @@ -164,4 +164,25 @@ void should_response_forbidden_when_listing_and_no_auth() throws Exception {
// then
resultActions.andExpect(status().isForbidden());
}

@Test
@WithMockUser
void should_update_status() throws Exception {
// given
JobApplicationDto applyExpected = new JobApplicationDto(12L, 123L, "title", "company", "http://image.url", CHASED.name(), "Paris (75)", CDD);
JobApplicationRequest request = new JobApplicationRequest(123L, CHASED);
String json = gson.toJson(request);
given(applicationService.updateApplyForCurrentUser(any())).willReturn(applyExpected);

// when
final ResultActions resultActions = mvc.perform(patch(APPLY_BASE_URI).content(json).accept(APPLICATION_JSON).contentType(APPLICATION_JSON)).andDo(print());

// then
resultActions
.andExpect(jsonPath("$.id", is(applyExpected.getId().intValue())))
.andExpect(jsonPath("$.jobId", is(applyExpected.getJobId().intValue())))
.andExpect(jsonPath("$.statusCode", is(applyExpected.getStatusCode())));

verify(applicationService, times(1)).updateApplyForCurrentUser(any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import java.util.Optional;

import static com.dynonuggets.refonteimplicaction.model.ApplyStatusEnum.*;
import static com.dynonuggets.refonteimplicaction.model.ContractTypeEnum.CDD;
import static com.dynonuggets.refonteimplicaction.model.ContractTypeEnum.CDI;
import static com.dynonuggets.refonteimplicaction.utils.Message.APPLY_ALREADY_EXISTS_FOR_JOB;
import static com.dynonuggets.refonteimplicaction.utils.Message.JOB_NOT_FOUND_MESSAGE;
import static com.dynonuggets.refonteimplicaction.utils.Message.*;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand All @@ -36,7 +36,7 @@
class JobApplicationServiceTest extends ControllerIntegrationTestBase {

@Mock
JobApplicationRepository applyRepostitory;
JobApplicationRepository applyRepository;

@Mock
JobPostingRepository jobRepository;
Expand All @@ -57,10 +57,10 @@ void should_create_apply() {
JobPosting job = new JobPosting(123L, Company.builder().id(23L).build(), "Mon super job", "Il est trop cool", "Blablabla", "Paris", "140k", null, CDI, Instant.now(), false);
final User currentUser = User.builder().id(45L).build();
JobApplication jobApplication = new JobApplication(67L, job, currentUser, request.getStatus(), Instant.now(), false);
JobApplicationDto expectedDto = new JobApplicationDto(jobApplication.getId(), jobApplication.getJob().getId(), jobApplication.getJob().getTitle(), jobApplication.getJob().getCompany().getName(), jobApplication.getJob().getCompany().getLogo(), jobApplication.getStatus().name(), CDI);
JobApplicationDto expectedDto = new JobApplicationDto(jobApplication.getId(), jobApplication.getJob().getId(), jobApplication.getJob().getTitle(), jobApplication.getJob().getCompany().getName(), jobApplication.getJob().getCompany().getLogo(), jobApplication.getStatus().name(), "Paris (75)", CDI);
given(jobRepository.findById(anyLong())).willReturn(Optional.of(job));
given(authService.getCurrentUser()).willReturn(currentUser);
given(applyRepostitory.save(any())).willReturn(jobApplication);
given(applyRepository.save(any())).willReturn(jobApplication);
given(applyAdapter.toDto(any())).willReturn(expectedDto);

// when
Expand Down Expand Up @@ -98,12 +98,12 @@ void should_throw_exception_when_creating_with_already_applied_job() {
long jobId = 123L;
JobApplicationRequest request = new JobApplicationRequest(jobId, PENDING);
IllegalArgumentException expectedException = new IllegalArgumentException(String.format(APPLY_ALREADY_EXISTS_FOR_JOB, jobId));
final JobApplication apply = JobApplication.builder().build();
final JobApplication apply = JobApplication.builder().id(123L).build();
JobPosting job = new JobPosting(123L, Company.builder().id(23L).build(), "Mon super job", "Il est trop cool", "Blablabla", "Paris", "140k", null, CDI, Instant.now(), false);
User currentUser = User.builder().id(123L).build();
given(jobRepository.findById(anyLong())).willReturn(Optional.of(job));
given(applyRepostitory.findByJobAndUser_id(any(), anyLong())).willReturn(Optional.of(apply));
given(authService.getCurrentUser()).willReturn(currentUser);
given(applyRepository.findByJob_IdAndUser_id(anyLong(), anyLong())).willReturn(Optional.of(apply));

// when
final IllegalArgumentException actualException = assertThrows(IllegalArgumentException.class, () -> jobApplicationService.createApplyIfNotExists(request));
Expand All @@ -122,12 +122,48 @@ void should_return_all_users_apply() {
new JobApplication(3L, JobPosting.builder().id(14L).build(), currentUser, INTERVIEW, Instant.now(), false)
);
given(authService.getCurrentUser()).willReturn(currentUser);
given(applyRepostitory.findAllByUserAndArchiveIsFalse(any())).willReturn(expecteds);
given(applyRepository.findAllByUserAndArchiveIsFalse(any())).willReturn(expecteds);

// when
final List<JobApplicationDto> allAppliesForCurrentUser = jobApplicationService.getAllAppliesForCurrentUser();

// then
assertThat(allAppliesForCurrentUser.size()).isEqualTo(expecteds.size());
}

@Test
void should_update_given_apply_for_current_user_if_apply_exists() {
// given
JobApplicationRequest request = new JobApplicationRequest(123L, INTERVIEW);
User currentUser = User.builder().id(123L).build();
final JobApplication jobApplication = new JobApplication(1L, JobPosting.builder().id(12L).build(), currentUser, PENDING, Instant.now(), false);
final JobApplication jobApplicationUpdate = new JobApplication(1L, JobPosting.builder().id(12L).build(), currentUser, PENDING, Instant.now(), false);
final JobApplicationDto expectedDto = new JobApplicationDto(1L, 12L, "JobTitle", "companyName", "http://image.url", INTERVIEW.name(), "Paris", CDD);
given(authService.getCurrentUser()).willReturn(currentUser);
given(applyRepository.findByJob_IdAndUser_id(anyLong(), anyLong())).willReturn(Optional.of(jobApplication));
given(applyRepository.save(any())).willReturn(jobApplicationUpdate);
given(applyAdapter.toDto(any())).willReturn(expectedDto);

// when
final JobApplicationDto actualDto = jobApplicationService.updateApplyForCurrentUser(request);

// then
assertThat(actualDto).usingRecursiveComparison().isEqualTo(expectedDto);
}

@Test
void should_throw_exception_when_update_and_job_not_exists() {
JobApplicationRequest request = new JobApplicationRequest(123L, INTERVIEW);
User currentUser = User.builder().id(123L).build();
given(authService.getCurrentUser()).willReturn(currentUser);
final NotFoundException expectedException = new NotFoundException(String.format(APPLY_NOT_FOUND_WITH_JOB_AND_USER, request
.getJobId(), currentUser.getId()));
given(applyRepository.findByJob_IdAndUser_id(anyLong(), anyLong())).willThrow(expectedException);

// when
final NotFoundException actualException = assertThrows(NotFoundException.class, () -> jobApplicationService.updateApplyForCurrentUser(request));

// then
assertThat(actualException.getMessage()).isEqualTo(expectedException.getMessage());
}
}
4 changes: 3 additions & 1 deletion frontend-implicaction/src/app/board/board.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ export class BoardComponent implements OnInit {
event.currentIndex
);
const jobApply = event.container.data[event.currentIndex];
jobApply.statusCode = statusCode;
this.jobBoardService
.updateApply({jobId: jobApply.jobId, status: statusCode})
.subscribe(jobApplyUpdate => jobApply.statusCode = jobApplyUpdate.statusCode);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {ApplyStatusCode} from '../enums/apply-status-enum';

export interface JobApplicationRequest {
id?: string;
jobId: string;
jobId?: string;
status: ApplyStatusCode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ export class JobBoardService {
getAllForCurrentUser(): Observable<JobApplication[]> {
return this.http.get<JobApplication[]>(this.apiEndpointService.getAllApplicationForCurrentUser());
}

updateApply(jobApplicationRequest: JobApplicationRequest): Observable<JobApplication> {
return this.http.patch<JobApplication>(this.apiEndpointService.updateApplicationStatus(), jobApplicationRequest);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -315,16 +315,6 @@ export class ApiEndpointsService {
return ApiEndpointsService.createUrlWithPageable(Uris.GROUP.BASE_URI, pageable);
}

private concatCriterias(criteria: Criteria, pageable: Pageable<any>): any {
return {
...criteria,
rows: pageable.rows,
page: pageable.page,
sortBy: pageable.sortBy,
sortOrder: pageable.sortOrder
};
}

/**
* JOB APPLICATION
*/
Expand All @@ -337,6 +327,20 @@ export class ApiEndpointsService {
return ApiEndpointsService.createUrl(Uris.JOB_APPLICATION.BASE_URI);
}

updateApplicationStatus(): string {
return ApiEndpointsService.createUrl(Uris.JOB_APPLICATION.BASE_URI);
}

private concatCriterias(criteria: Criteria, pageable: Pageable<any>): any {
return {
...criteria,
rows: pageable.rows,
page: pageable.page,
sortBy: pageable.sortBy,
sortOrder: pageable.sortOrder
};
}

/**
* Ajoute les attributs filtrés d'un objet de paramétrage de requête à un QueryStringParameters
* @return qs le QueryStringParameters modifié
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export class JobsListComponent extends BaseWithPaginationComponent<JobPosting, J
this.pageable.sortBy = JobSortEnum.DATE_DESC.sortBy;
this.selectedOrderCode = JobSortEnum.DATE_DESC.code;

// réinitialisation systématique du filtre au chargement du composant
this.filterService.criteria = {};

this.filterService
.observe()
.subscribe(criteria => {
Expand Down