external plan user invitation fixes

(cherry picked from commit d0cd1719a9)
This commit is contained in:
CITE\amentis 2024-10-08 14:05:48 +03:00
parent fabeca2631
commit 6c94ccadf9
12 changed files with 113 additions and 35 deletions

View File

@ -18,6 +18,9 @@ public class PlanInvitationEntity {
@XmlAttribute(name = "plan-role")
private PlanUserRole role;
@XmlAttribute(name = "section")
private UUID sectionId;
public String getEmail() {
return email;
}
@ -41,4 +44,12 @@ public class PlanInvitationEntity {
public void setRole(PlanUserRole role) {
this.role = role;
}
public UUID getSectionId() {
return sectionId;
}
public void setSectionId(UUID sectionId) {
this.sectionId = sectionId;
}
}

View File

@ -0,0 +1,26 @@
package org.opencdmp.model;
import java.util.UUID;
public class PlanInvitationResult {
private UUID planId;
private Boolean isAlreadyAccepted;
public UUID getPlanId() {
return planId;
}
public void setPlanId(UUID planId) {
this.planId = planId;
}
public Boolean getIsAlreadyAccepted() {
return isAlreadyAccepted;
}
public void setIsAlreadyAccepted(Boolean alreadyAccepted) {
isAlreadyAccepted = alreadyAccepted;
}
}

View File

@ -12,6 +12,9 @@ public class PlanInvitation {
private UUID planId;
public static final String _planId = "planId";
private UUID sectionId;
public static final String _sectionId = "sectionId";
private PlanUserRole role;
public static final String _role = "role";
@ -32,6 +35,14 @@ public class PlanInvitation {
this.planId = planId;
}
public UUID getSectionId() {
return sectionId;
}
public void setSectionId(UUID sectionId) {
this.sectionId = sectionId;
}
public PlanUserRole getRole() {
return role;
}

View File

@ -47,6 +47,7 @@ public class PlanInvitationBuilder extends BaseBuilder<PlanInvitation, PlanInvit
PlanInvitation m = new PlanInvitation();
if (fields.hasField(this.asIndexer(PlanInvitation._email))) m.setEmail(d.getEmail());
if (fields.hasField(this.asIndexer(PlanInvitation._planId))) m.setPlanId(d.getPlanId());
if (fields.hasField(this.asIndexer(PlanInvitation._sectionId))) m.setSectionId(d.getSectionId());
if (fields.hasField(this.asIndexer(PlanInvitation._role))) m.setRole(d.getRole());
models.add(m);

View File

@ -25,13 +25,18 @@ public class PlanInvitationPersist {
public static final String _planId = "planId";
private UUID sectionId;
public static final String _sectionId = "sectionId";
private PlanUserRole role;
public static final String _role = "role";
public PlanInvitationPersist(String email, UUID planId, PlanUserRole role) {
public PlanInvitationPersist(String email, UUID planId, UUID sectionId, PlanUserRole role) {
this.email = email;
this.planId = planId;
this.sectionId = sectionId;
this.role = role;
}
@ -51,6 +56,14 @@ public class PlanInvitationPersist {
this.planId = planId;
}
public UUID getSectionId() {
return sectionId;
}
public void setSectionId(UUID sectionId) {
this.sectionId = sectionId;
}
public PlanUserRole getRole() {
return role;
}

View File

@ -126,6 +126,7 @@ public class ActionConfirmationServiceImpl implements ActionConfirmationService
data.setEmail(persist.getEmail());
data.setRole(persist.getRole());
data.setPlanId(persist.getPlanId());
data.setSectionId(persist.getSectionId());
return data;
}

View File

@ -8,6 +8,7 @@ import gr.cite.tools.fieldset.FieldSet;
import jakarta.xml.bind.JAXBException;
import org.opencdmp.commons.types.plan.importexport.PlanImportExport;
import org.opencdmp.filetransformerbase.models.misc.PreprocessingPlanModel;
import org.opencdmp.model.PlanInvitationResult;
import org.opencdmp.model.PlanUser;
import org.opencdmp.model.PlanValidationResult;
import org.opencdmp.model.plan.Plan;
@ -50,7 +51,7 @@ public interface PlanService {
void inviteUserOrAssignUsers(UUID id, List<PlanUserPersist> users) throws InvalidApplicationException, JAXBException, IOException;
UUID planInvitationAccept(String token) throws InvalidApplicationException, IOException;
PlanInvitationResult planInvitationAccept(String token) throws InvalidApplicationException, IOException;
PlanImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize, boolean isPublic) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException;

View File

@ -69,10 +69,7 @@ import org.opencdmp.integrationevent.outbox.annotationentityremoval.AnnotationEn
import org.opencdmp.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import org.opencdmp.model.PlanDescriptionTemplate;
import org.opencdmp.model.PlanUser;
import org.opencdmp.model.PlanValidationResult;
import org.opencdmp.model.PublicPlan;
import org.opencdmp.model.*;
import org.opencdmp.model.builder.PlanUserBuilder;
import org.opencdmp.model.builder.description.DescriptionBuilder;
import org.opencdmp.model.builder.plan.PlanBuilder;
@ -1975,7 +1972,7 @@ public class PlanServiceImpl implements PlanService {
this.sendPlanInvitationExistingUser(user.getUser(), plan, user.getRole());
}
}else if (user.getEmail() != null) {
this.sendPlanInvitationExternalUser(user.getEmail(),plan, user.getRole());
this.sendPlanInvitationExternalUser(user.getEmail(),plan, user.getRole(), user.getSectionId());
}
}
@ -2013,8 +2010,8 @@ public class PlanServiceImpl implements PlanService {
this.eventHandler.handle(event);
}
private void sendPlanInvitationExternalUser(String email, PlanEntity plan, PlanUserRole role) throws JAXBException, InvalidApplicationException {
String token = this.createActionConfirmation(email, plan, role);
private void sendPlanInvitationExternalUser(String email, PlanEntity plan, PlanUserRole role, UUID sectionId) throws JAXBException, InvalidApplicationException {
String token = this.createActionConfirmation(email, plan, role, sectionId);
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
@ -2038,12 +2035,12 @@ public class PlanServiceImpl implements PlanService {
this.eventHandler.handle(event);
}
private String createActionConfirmation(String email, PlanEntity plan, PlanUserRole role) throws JAXBException, InvalidApplicationException {
private String createActionConfirmation(String email, PlanEntity plan, PlanUserRole role, UUID sectionId) throws JAXBException, InvalidApplicationException {
ActionConfirmationPersist persist = new ActionConfirmationPersist();
persist.setType(ActionConfirmationType.PlanInvitation);
persist.setStatus(ActionConfirmationStatus.Requested);
persist.setToken(UUID.randomUUID().toString());
persist.setPlanInvitation(new PlanInvitationPersist(email, plan.getId(), role));
persist.setPlanInvitation(new PlanInvitationPersist(email, plan.getId(), sectionId, role));
persist.setExpiresAt(Instant.now().plusSeconds(this.notificationProperties.getEmailExpirationTimeSeconds()));
this.validatorFactory.validator(ActionConfirmationPersist.ActionConfirmationPersistValidator.class).validateForce(persist);
this.actionConfirmationService.persist(persist, null);
@ -2051,23 +2048,29 @@ public class PlanServiceImpl implements PlanService {
return persist.getToken();
}
public UUID planInvitationAccept(String token) throws InvalidApplicationException {
public PlanInvitationResult planInvitationAccept(String token) throws InvalidApplicationException {
PlanInvitationResult planInvitationResult = new PlanInvitationResult();
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.PlanInvitation).isActive(IsActive.Active).first();
if (action == null){
throw new MyValidationException(this.errors.getTokenNotExist().getCode(), this.errors.getTokenNotExist().getMessage());
}
if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){
throw new MyValidationException(this.errors.getPlanInvitationAlreadyConfirmed().getCode(), this.errors.getPlanInvitationAlreadyConfirmed().getMessage());
}
if (action.getExpiresAt().compareTo(Instant.now()) < 0){
throw new MyValidationException(this.errors.getRequestHasExpired().getCode(), this.errors.getRequestHasExpired().getMessage());
}
PlanInvitationEntity planInvitation = this.xmlHandlingService.fromXmlSafe(PlanInvitationEntity.class, action.getData());
if (planInvitation == null) {
throw new MyApplicationException("plan invitation don't exist");
}
if (action.getStatus().equals(ActionConfirmationStatus.Accepted)){
UserContactInfoEntity contactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(this.userScope.getUserId()).values(planInvitation.getEmail()).types(ContactInfoType.Email).first();
if (contactInfoEntity == null){
throw new MyValidationException(this.errors.getAnotherUserToken().getCode(), this.errors.getAnotherUserToken().getMessage());
}
planInvitationResult.setPlanId(planInvitation.getPlanId());
planInvitationResult.setIsAlreadyAccepted(true);
return planInvitationResult;
}
if (action.getExpiresAt().compareTo(Instant.now()) < 0){
throw new MyValidationException(this.errors.getRequestHasExpired().getCode(), this.errors.getRequestHasExpired().getMessage());
}
UserContactInfoEntity contactInfoEntity = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(this.userScope.getUserId()).values(planInvitation.getEmail()).types(ContactInfoType.Email).first();
if (contactInfoEntity == null){
@ -2081,13 +2084,16 @@ public class PlanServiceImpl implements PlanService {
data.setRole(planInvitation.getRole());
data.setUserId(this.userScope.getUserIdSafe());
data.setPlanId(planInvitation.getPlanId());
data.setSectionId(planInvitation.getSectionId());
this.entityManager.persist(data);
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
this.annotationEntityTouchedIntegrationEventHandler.handlePlan(planInvitation.getPlanId());
return planInvitation.getPlanId();
planInvitationResult.setPlanId(planInvitation.getPlanId());
planInvitationResult.setIsAlreadyAccepted(false);
return planInvitationResult;
}
//region Export

View File

@ -31,10 +31,7 @@ import org.opencdmp.controllers.swagger.annotation.Swagger400;
import org.opencdmp.controllers.swagger.annotation.Swagger404;
import org.opencdmp.controllers.swagger.annotation.SwaggerCommonErrorResponses;
import org.opencdmp.filetransformerbase.models.misc.PreprocessingPlanModel;
import org.opencdmp.model.DescriptionsToBeFinalized;
import org.opencdmp.model.PlanUser;
import org.opencdmp.model.PlanValidationResult;
import org.opencdmp.model.PublicPlan;
import org.opencdmp.model.*;
import org.opencdmp.model.builder.PublicPlanBuilder;
import org.opencdmp.model.builder.plan.PlanBuilder;
import org.opencdmp.model.censorship.PublicPlanCensor;
@ -469,16 +466,16 @@ public class PlanController {
@OperationWithTenantHeader(summary = "Accept an invitation token for a plan by token")
@Transactional
@Hidden
public UUID acceptInvitation(@PathVariable("token") String token) throws InvalidApplicationException, JAXBException, IOException {
public PlanInvitationResult acceptInvitation(@PathVariable("token") String token) throws InvalidApplicationException, JAXBException, IOException {
logger.debug(new MapLogEntry("inviting users to plan").And("token", token));
UUID planId = this.planService.planInvitationAccept(token);
PlanInvitationResult planInvitationResult = this.planService.planInvitationAccept(token);
this.auditService.track(AuditableAction.Plan_Invite_Accept, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("token", token)
));
return planId;
return planInvitationResult;
}
@RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml")

View File

@ -183,5 +183,10 @@ export interface PlanUserInvitePersist {
}
//
// Public
// Invitation
//
export interface PlanInvitationResult {
planId: Guid;
isAlreadyAccepted: boolean;
}

View File

@ -15,7 +15,7 @@ import { catchError, map } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { BaseHttpParams } from '../../../../common/http/base-http-params';
import { InterceptorType } from '../../../../common/http/interceptors/interceptor-type';
import { ClonePlanPersist, Plan, PlanPersist, PlanUser, PlanUserInvitePersist, PlanUserPersist, PlanUserRemovePersist, NewVersionPlanPersist, PublicPlan } from '../../model/plan/plan';
import { ClonePlanPersist, Plan, PlanPersist, PlanUser, PlanUserInvitePersist, PlanUserPersist, PlanUserRemovePersist, NewVersionPlanPersist, PublicPlan, PlanInvitationResult } from '../../model/plan/plan';
import { AuthService } from '../auth/auth.service';
import { ConfigurationService } from '../configuration/configuration.service';
import { BaseHttpV2Service } from '../http/base-http-v2.service';
@ -162,10 +162,10 @@ export class PlanService {
catchError((error: any) => throwError(error)));
}
acceptInvitation(token: string): Observable<Guid> {
acceptInvitation(token: string): Observable<PlanInvitationResult> {
const url = `${this.apiBase}/token/${token}/invite-accept`;
return this.http.get<Guid>(url).pipe(catchError((error: any) => throwError(error)));
return this.http.get<PlanInvitationResult>(url).pipe(catchError((error: any) => throwError(error)));
}
downloadXML(id: Guid): Observable<HttpResponse<Blob>> {

View File

@ -2,11 +2,12 @@ import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '@app/core/services/auth/auth.service';
import { SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { PlanService } from '@app/core/services/plan/plan.service';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
@Component({
@ -21,7 +22,9 @@ export class InvitationAcceptedComponent extends BaseComponent implements OnInit
private router: Router,
private authentication: AuthService,
protected routerUtils: RouterUtilsService,
private httpErrorHandlingService: HttpErrorHandlingService
protected language: TranslateService,
private httpErrorHandlingService: HttpErrorHandlingService,
private uiNotificationService: UiNotificationService
) { super(); }
ngOnInit(): void {
@ -35,7 +38,10 @@ export class InvitationAcceptedComponent extends BaseComponent implements OnInit
this.planService.acceptInvitation(token)
.pipe(takeUntil(this._destroyed))
.subscribe(result => {
this.router.navigate([this.routerUtils.generateUrl('plans/edit/' + result)]);
if (result?.isAlreadyAccepted == false) {
this.uiNotificationService.snackBarNotification(this.language.instant('PLAN-USER-INVITATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
}
this.router.navigate([this.routerUtils.generateUrl('plans/overview/' + result?.planId)]);
},
error => this.onCallbackError(error));
}else{