Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…atung-userService into OB-8336-remove-invalid-rocketchat-sessions
  • Loading branch information
tkuzynow committed Feb 7, 2024
2 parents d1a6f04 + d3ce151 commit d995e16
Show file tree
Hide file tree
Showing 52 changed files with 813 additions and 134 deletions.
2 changes: 2 additions & 0 deletions api/useradminservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,8 @@ components:
type: integer
tenantName:
type: string
displayName:
type: string

CreateAdminDTO:
type: object
Expand Down
22 changes: 22 additions & 0 deletions services/appointmentService.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ paths:
schema:
$ref: '#/components/schemas/CalcomUser'
/consultants/{consultantId}:
patch:
tags:
- consultant
summary: Patch consultant to cal.com with consultant object
operationId: patchConsultant
parameters:
- name: consultantId
in: path
description: ID of onber consultant
required: true
schema:
type: string
requestBody:
description: consultant object that needs to be patched to cal.com
content:
application/json:
schema:
$ref: './../api/useradminservice.yaml#/components/schemas/ConsultantDTO'
required: true
responses:
'200':
description: successful operation
put:
tags:
- consultant
Expand Down
9 changes: 9 additions & 0 deletions services/mailservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ components:
example: "[email protected]"
language:
$ref: '#/components/schemas/LanguageCode'
dialect:
$ref: '#/components/schemas/Dialect'
templateData:
type: array
items:
Expand Down Expand Up @@ -116,3 +118,10 @@ components:
so, sq, sr, ss, st, su, sv, sw, ta, te, tg, th, ti, tk, tl, tn, to, tr, ts, tt, tw,
ty, ug, uk, ur, uz, ve, vi, vo, wa, wo, xh, yi, yo, za, zh, zu
]

Dialect:
type: string
default: formal
enum: [
formal, informal
]
15 changes: 6 additions & 9 deletions src/main/java/de/caritas/cob/userservice/api/AccountManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import de.caritas.cob.userservice.api.port.out.SessionRepository;
import de.caritas.cob.userservice.api.port.out.UserRepository;
import de.caritas.cob.userservice.api.service.agency.AgencyService;
import de.caritas.cob.userservice.api.service.appointment.AppointmentService;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -51,6 +52,10 @@ public class AccountManager implements AccountManaging {

private final SessionRepository sessionRepository;

private final AppointmentService appointmentService;

private final PatchConsultantSaga patchConsultantSaga;

@Override
public Optional<Map<String, Object>> findConsultant(String id) {
var userMap = new HashMap<String, Object>();
Expand Down Expand Up @@ -173,15 +178,7 @@ private Map<String, Object> patchAdviceSeeker(User adviceSeeker, Map<String, Obj

private Map<String, Object> patchConsultant(Consultant consultant, Map<String, Object> patchMap) {
var patchedConsultant = userServiceMapper.consultantOf(consultant, patchMap);
var savedConsultant = consultantRepository.save(patchedConsultant);

userServiceMapper
.displayNameOf(patchMap)
.ifPresent(
displayName ->
messageClient.updateUser(savedConsultant.getRocketChatId(), displayName));

return userServiceMapper.mapOf(savedConsultant, patchMap);
return patchConsultantSaga.executeTransactional(patchedConsultant, patchMap);
}

private Map<String, Object> findByDbConsultant(Consultant dbConsultant) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package de.caritas.cob.userservice.api;

import com.google.common.collect.Lists;
import de.caritas.cob.userservice.api.admin.service.consultant.TransactionalStep;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionException;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionInfo;
import de.caritas.cob.userservice.api.model.Consultant;
import de.caritas.cob.userservice.api.port.out.ConsultantRepository;
import de.caritas.cob.userservice.api.port.out.MessageClient;
import de.caritas.cob.userservice.api.service.appointment.AppointmentService;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Slf4j
public class PatchConsultantSaga {

private final ConsultantRepository consultantRepository;

private final UserServiceMapper userServiceMapper;

private final MessageClient messageClient;

private final AppointmentService appointmentService;

private final PatchConsultantSagaRollbackHandler patchConsultantSagaRollbackHandler;

@Transactional
public Map<String, Object> executeTransactional(
Consultant patchedConsultant, Map<String, Object> patchMap) {
Consultant savedConsultant = saveConsultant(patchedConsultant);
userServiceMapper
.encodedDisplayNameOf(patchMap)
.ifPresent(
encodedUserName -> updateUserInRocketChatOrRollback(savedConsultant, encodedUserName));

userServiceMapper
.displayNameOf(patchMap)
.ifPresent(
displayName ->
patchConsultantInAppointmentServiceOrRollback(savedConsultant, displayName));
return userServiceMapper.mapOf(savedConsultant, patchMap);
}

private void patchConsultantInAppointmentServiceOrRollback(
Consultant savedConsultant, String displayName) {

try {
appointmentService.patchConsultant(savedConsultant.getId(), displayName);
} catch (Exception e) {
log.error(
"Error while patching consultant in appointment service. Will rollback patchConsultantSaga.",
e);
patchConsultantSagaRollbackHandler.rollbackUpdateUserInRocketchat(savedConsultant);
// rollback on MariaDB will be handled automatically by spring due to @Transactional
throw new DistributedTransactionException(
e,
DistributedTransactionInfo.builder()
.completedTransactionalOperations(
Lists.newArrayList(
TransactionalStep.SAVE_CONSULTANT_IN_MARIADB,
TransactionalStep.UPDATE_ROCKET_CHAT_USER_DISPLAY_NAME))
.name("patchConsultant")
.failedStep(TransactionalStep.PATCH_APPOINTMENT_SERVICE_CONSULTANT)
.build());
}
}

private void updateUserInRocketChatOrRollback(Consultant savedConsultant, String displayName) {
try {
if (savedConsultant.getRocketChatId() != null) {
messageClient.updateUser(savedConsultant.getRocketChatId(), displayName);
}
} catch (Exception e) {
log.error(
"Error while updating consultant in rocketchat. Will rollback patchConsultantSaga.", e);
// rollback will be handled automatically by spring due to @Transactional
throw new DistributedTransactionException(
e,
DistributedTransactionInfo.builder()
.completedTransactionalOperations(
Lists.newArrayList(TransactionalStep.SAVE_CONSULTANT_IN_MARIADB))
.name("patchConsultant")
.failedStep(TransactionalStep.UPDATE_ROCKET_CHAT_USER_DISPLAY_NAME)
.build());
}
}

private Consultant saveConsultant(Consultant patchedConsultant) {
return consultantRepository.save(patchedConsultant);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.caritas.cob.userservice.api;

import com.google.common.collect.Lists;
import de.caritas.cob.userservice.api.admin.service.consultant.TransactionalStep;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionException;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionInfo;
import de.caritas.cob.userservice.api.model.Consultant;
import de.caritas.cob.userservice.api.port.out.MessageClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class PatchConsultantSagaRollbackHandler {

private final MessageClient messageClient;

public void rollbackUpdateUserInRocketchat(Consultant savedConsultant) {
try {
var originalDisplayName =
messageClient
.findUser(savedConsultant.getRocketChatId())
.get()
.get("displayName")
.toString();
messageClient.updateUser(savedConsultant.getRocketChatId(), originalDisplayName);
} catch (Exception e) {
log.error("Error while rolling back consultant", e);
throw new DistributedTransactionException(
e,
DistributedTransactionInfo.builder()
.completedTransactionalOperations(Lists.newArrayList())
.name("patchConsultant")
.failedStep(TransactionalStep.ROLLBACK_UPDATE_ROCKET_CHAT_USER_DISPLAY_NAME)
.build());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ public Consultant consultantOf(Consultant consultant, Map<String, Object> patchM
return consultant;
}

public Optional<String> displayNameOf(Map<String, Object> patchMap) {
public Optional<String> encodedDisplayNameOf(Map<String, Object> patchMap) {
if (patchMap.containsKey("displayName")) {
var displayName = (String) patchMap.get("displayName");
var encodedDisplayName = usernameTranscoder.encodeUsername(displayName);
Expand All @@ -411,6 +411,15 @@ public Optional<String> displayNameOf(Map<String, Object> patchMap) {
return Optional.empty();
}

public Optional<String> displayNameOf(Map<String, Object> patchMap) {
if (patchMap.containsKey("displayName")) {
var displayName = (String) patchMap.get("displayName");
return Optional.of(displayName);
}

return Optional.empty();
}

public User adviceSeekerOf(User adviceSeeker, Map<String, Object> patchMap) {
if (patchMap.containsKey("email")) {
adviceSeeker.setEmail((String) patchMap.get("email"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import de.caritas.cob.userservice.api.exception.httpresponses.ConflictException;
import de.caritas.cob.userservice.api.exception.httpresponses.CreateEnquiryMessageException;
import de.caritas.cob.userservice.api.exception.httpresponses.CustomValidationHttpStatusException;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionException;
import de.caritas.cob.userservice.api.exception.httpresponses.ForbiddenException;
import de.caritas.cob.userservice.api.exception.httpresponses.InternalServerErrorException;
import de.caritas.cob.userservice.api.exception.httpresponses.NoContentException;
Expand Down Expand Up @@ -55,6 +56,14 @@ public ResponseEntity<Object> handleJPAConstraintViolationException(
return handleExceptionInternal(ex, null, new HttpHeaders(), HttpStatus.CONFLICT, request);
}

@ExceptionHandler({DistributedTransactionException.class})
public ResponseEntity<Object> handleDistributedTransactionException(
final DistributedTransactionException ex, final WebRequest request) {
log.error("Distributed transaction failed to complete", ex);
return handleExceptionInternal(
ex, null, ex.getCustomHttpHeaders(), HttpStatus.FAILED_DEPENDENCY, request);
}

@ExceptionHandler({CreateEnquiryMessageException.class})
public ResponseEntity<Object> handleCreateEnquiryMessageException(
final CreateEnquiryMessageException ex, final WebRequest request) {
Expand Down Expand Up @@ -88,7 +97,8 @@ public ResponseEntity<Object> handleCustomBadRequest(
final CustomValidationHttpStatusException ex, final WebRequest request) {
ex.executeLogging();

return handleExceptionInternal(ex, null, ex.getCustomHttpHeader(), ex.getHttpStatus(), request);
return handleExceptionInternal(
ex, null, ex.getCustomHttpHeaders(), ex.getHttpStatus(), request);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public ConsultantAdminFilterTenantAwareService(
super(entityManagerFactory);
}

@Override
protected FullTextQuery buildFilteredQuery(
ConsultantFilter consultantFilter, FullTextEntityManager fullTextEntityManager) {

Expand All @@ -53,8 +54,10 @@ protected FullTextQuery buildFilteredQuery(
.onConsultantFilter(consultantFilter)
.buildQuery();

Query resultQuery = queryBuilder.bool().must(tenantQuery).must(query).createQuery();

Query resultQuery =
TenantContext.isTechnicalOrSuperAdminContext()
? query
: queryBuilder.bool().must(tenantQuery).must(query).createQuery();
return fullTextEntityManager.createFullTextQuery(resultQuery, Consultant.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import de.caritas.cob.userservice.api.admin.service.consultant.create.ConsultantCreatorService;
import de.caritas.cob.userservice.api.admin.service.consultant.delete.ConsultantPreDeletionService;
import de.caritas.cob.userservice.api.admin.service.consultant.update.ConsultantUpdateService;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionException;
import de.caritas.cob.userservice.api.exception.httpresponses.DistributedTransactionInfo;
import de.caritas.cob.userservice.api.exception.httpresponses.NoContentException;
import de.caritas.cob.userservice.api.exception.httpresponses.NotFoundException;
import de.caritas.cob.userservice.api.helper.AuthenticatedUser;
Expand All @@ -23,6 +25,7 @@
import de.caritas.cob.userservice.api.port.out.ConsultantRepository;
import de.caritas.cob.userservice.api.port.out.SessionRepository;
import de.caritas.cob.userservice.api.service.appointment.AppointmentService;
import java.util.List;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -69,14 +72,36 @@ public ConsultantAdminResponseDTO findConsultantById(String consultantId) {
* @return the generated and persisted {@link Consultant} representation as {@link
* ConsultantAdminResponseDTO}
*/
public ConsultantAdminResponseDTO createNewConsultant(CreateConsultantDTO createConsultantDTO) {
public ConsultantAdminResponseDTO createNewConsultant(CreateConsultantDTO createConsultantDTO)
throws DistributedTransactionException {
Consultant newConsultant =
this.consultantCreatorService.createNewConsultant(createConsultantDTO);
List<TransactionalStep> completedSteps =
Lists.newArrayList(
TransactionalStep.CREATE_ACCOUNT_IN_KEYCLOAK,
TransactionalStep.CREATE_ACCOUNT_IN_ROCKETCHAT,
TransactionalStep.CREATE_CONSULTANT_IN_MARIADB);

ConsultantAdminResponseDTO consultantAdminResponseDTO =
ConsultantResponseDTOBuilder.getInstance(newConsultant).buildResponseDTO();

this.appointmentService.createConsultant(consultantAdminResponseDTO);
try {
this.appointmentService.createConsultant(consultantAdminResponseDTO);
} catch (Exception e) {
log.error(
"User with id {}, who has roles {}, has created a consultant with id {} but the appointment service returned an error: {}",
authenticatedUser.getUserId(),
authenticatedUser.getRoles(),
newConsultant.getId(),
e.getMessage());
this.consultantCreatorService.rollbackCreateNewConsultant(newConsultant);
throw new DistributedTransactionException(
e,
new DistributedTransactionInfo(
"createNewConsultant",
completedSteps,
TransactionalStep.CREATE_ACCOUNT_IN_CALCOM_OR_APPOINTMENTSERVICE));
}

return consultantAdminResponseDTO;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.caritas.cob.userservice.api.admin.service.consultant;

public enum TransactionalStep {
CREATE_ACCOUNT_IN_KEYCLOAK,
CREATE_CONSULTANT_IN_MARIADB,

CREATE_ACCOUNT_IN_ROCKETCHAT,

CREATE_ACCOUNT_IN_CALCOM_OR_APPOINTMENTSERVICE,

SAVE_CONSULTANT_IN_MARIADB,
ROLLBACK_CONSULTANT_IN_MARIADB,
UPDATE_ROCKET_CHAT_USER_DISPLAY_NAME,

ROLLBACK_UPDATE_ROCKET_CHAT_USER_DISPLAY_NAME,

PATCH_APPOINTMENT_SERVICE_CONSULTANT;
}
Loading

0 comments on commit d995e16

Please sign in to comment.