Skip to content

Commit

Permalink
[TECH] Migrer la route DELETE /api/organizations/{id}/invitations/{or…
Browse files Browse the repository at this point in the history
…ganizationInvitationId} et /api/admin/organizations/{id}/invitations/{organizationInvitationId} dans src/team (PIX-13262)

 #9444
  • Loading branch information
pix-service-auto-merge committed Jul 8, 2024
2 parents 0f2fa44 + 8869598 commit bd2f2ec
Show file tree
Hide file tree
Showing 24 changed files with 302 additions and 250 deletions.
2 changes: 2 additions & 0 deletions api/config/server-setup-error-handling.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { prescriptionDomainErrorMappingConfiguration } from '../src/prescription
import { schoolDomainErrorMappingConfiguration } from '../src/school/application/http-error-mapper-configuration.js';
import { domainErrorMapper } from '../src/shared/application/domain-error-mapper.js';
import * as sharedPreResponseUtils from '../src/shared/application/pre-response-utils.js';
import { teamDomainErrorMappingConfiguration } from '../src/team/application/http-error-mapper-configuration.js';

const setupErrorHandling = function (server) {
const configuration = [
...authenticationDomainErrorMappingConfiguration,
...organizationalEntitiesDomainErrorMappingConfiguration,
...teamDomainErrorMappingConfiguration,
...certificationDomainErrorMappingConfiguration,
...devcompDomainErrorMappingConfiguration,
...evaluationDomainErrorMappingConfiguration,
Expand Down
4 changes: 0 additions & 4 deletions api/lib/application/error-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,10 +287,6 @@ function _mapToHttpError(error) {
return new HttpErrors.ConflictError(error.message);
}

if (error instanceof DomainErrors.UncancellableOrganizationInvitationError) {
return new HttpErrors.UnprocessableEntityError(error.message);
}

if (error instanceof DomainErrors.UserShouldNotBeReconciledOnAnotherAccountError) {
return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta);
}
Expand Down
53 changes: 0 additions & 53 deletions api/lib/application/organizations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,35 +169,6 @@ const register = async function (server) {
],
},
},
{
method: 'DELETE',
path: '/api/admin/organizations/{id}/invitations/{organizationInvitationId}',
config: {
pre: [
{
method: (request, h) =>
securityPreHandlers.hasAtLeastOneAccessOf([
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
securityPreHandlers.checkAdminMemberHasRoleSupport,
securityPreHandlers.checkAdminMemberHasRoleMetier,
])(request, h),
assign: 'hasAuthorizationToAccessAdminScope',
},
],
validate: {
params: Joi.object({
id: identifiersType.organizationId,
organizationInvitationId: identifiersType.organizationInvitationId,
}),
},
handler: organizationController.cancelOrganizationInvitation,
tags: ['api', 'admin', 'invitations', 'cancel'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
"- Elle permet d'annuler une invitation envoyée mais non acceptée encore.",
],
},
},
{
method: 'POST',
path: '/api/admin/organizations/{id}/invitations',
Expand Down Expand Up @@ -453,30 +424,6 @@ const register = async function (server) {
tags: ['api', 'invitations'],
},
},
{
method: 'DELETE',
path: '/api/organizations/{id}/invitations/{organizationInvitationId}',
config: {
pre: [
{
method: securityPreHandlers.checkUserIsAdminInOrganization,
assign: 'isAdminInOrganization',
},
],
validate: {
params: Joi.object({
id: identifiersType.organizationId,
organizationInvitationId: identifiersType.organizationInvitationId,
}),
},
handler: organizationController.cancelOrganizationInvitation,
tags: ['api', 'invitations', 'cancel'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés en tant qu'admin d'une organisation**\n" +
"- Elle permet à l'administrateur de l'organisation d'annuler une invitation envoyée mais non acceptée encore.",
],
},
},
{
method: 'GET',
path: '/api/organizations/{id}/member-identities',
Expand Down
7 changes: 0 additions & 7 deletions api/lib/application/organizations/organization-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,6 @@ const resendInvitation = async function (request, h) {
return h.response(organizationInvitationSerializer.serialize(organizationInvitation));
};

const cancelOrganizationInvitation = async function (request, h) {
const organizationInvitationId = request.params.organizationInvitationId;
await usecases.cancelOrganizationInvitation({ organizationInvitationId });
return h.response().code(204);
};

const sendInvitationByLangAndRole = async function (request, h, dependencies = { organizationInvitationSerializer }) {
const organizationId = request.params.id;
const invitationInformation =
Expand Down Expand Up @@ -161,7 +155,6 @@ const findChildrenOrganizationsForAdmin = async function (

const organizationController = {
archiveOrganization,
cancelOrganizationInvitation,
create,
createInBatch,
findChildrenOrganizationsForAdmin,
Expand Down
10 changes: 0 additions & 10 deletions api/lib/domain/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,6 @@ class CancelledInvitationError extends DomainError {
}
}

class UncancellableOrganizationInvitationError extends DomainError {
constructor(
message = "L'invitation à cette organisation ne peut pas être annulée.",
code = 'UNCANCELLABLE_ORGANIZATION_INVITATION_CODE',
) {
super(message, code);
}
}

class CantImproveCampaignParticipationError extends DomainError {
constructor(message = 'Une campagne de collecte de profils ne peut pas être retentée.') {
super(message);
Expand Down Expand Up @@ -1084,7 +1075,6 @@ export {
TargetProfileCannotBeCreated,
TargetProfileInvalidError,
TooManyRows,
UncancellableOrganizationInvitationError,
UnexpectedUserAccountError,
UnknownCountryForStudentEnrolmentError,
UserAlreadyExistsWithAuthenticationMethodError,
Expand Down
12 changes: 12 additions & 0 deletions api/src/team/application/http-error-mapper-configuration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { HttpErrors } from '../../shared/application/http-errors.js';
import { DomainErrorMappingConfiguration } from '../../shared/application/models/domain-error-mapping-configuration.js';
import { UncancellableOrganizationInvitationError } from '../domain/errors.js';

const teamDomainErrorMappingConfiguration = [
{
name: UncancellableOrganizationInvitationError.name,
httpErrorFn: (error) => new HttpErrors.UnprocessableEntityError(error.message),
},
].map((domainErrorMappingConfiguration) => new DomainErrorMappingConfiguration(domainErrorMappingConfiguration));

export { teamDomainErrorMappingConfiguration };
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Joi from 'joi';

import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { organizationInvitationController } from './organization-invitation.controller.js';

export const organizationInvitationAdminRoutes = [
{
method: 'DELETE',
path: '/api/admin/organizations/{id}/invitations/{organizationInvitationId}',
config: {
pre: [
{
method: (request, h) =>
securityPreHandlers.hasAtLeastOneAccessOf([
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
securityPreHandlers.checkAdminMemberHasRoleSupport,
securityPreHandlers.checkAdminMemberHasRoleMetier,
])(request, h),
assign: 'hasAuthorizationToAccessAdminScope',
},
],
validate: {
params: Joi.object({
id: identifiersType.organizationId,
organizationInvitationId: identifiersType.organizationInvitationId,
}),
},
handler: (request, h) => organizationInvitationController.cancelOrganizationInvitation(request, h),
tags: ['team', 'api', 'admin', 'invitations', 'cancel'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
"- Elle permet d'annuler une invitation envoyée mais non acceptée encore.",
],
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import { usecases } from '../../domain/usecases/index.js';
import { organizationInvitationSerializer as organizationInvitationSerializer } from '../../infrastructure/serializers/jsonapi/organization-invitation.serializer.js';
import { serializer as scoOrganizationInvitationSerializer } from '../../infrastructure/serializers/jsonapi/sco-organization-invitation.serializer.js';

/**
*
* @param request
* @param h
* @returns {Promise<any>}
*/
const cancelOrganizationInvitation = async function (request, h) {
const organizationInvitationId = request.params.organizationInvitationId;
await usecases.cancelOrganizationInvitation({ organizationInvitationId });
return h.response().code(204);
};

/**
*
* @param request
Expand Down Expand Up @@ -55,6 +67,7 @@ const sendScoInvitation = async function (
};

export const organizationInvitationController = {
cancelOrganizationInvitation,
getOrganizationInvitation,
sendScoInvitation,
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Joi from 'joi';

import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { organizationInvitationController } from './organization-invitation.controller.js';

Expand All @@ -25,7 +26,7 @@ export const organizationInvitationRoutes = [
notes: [
"- Cette route permet d'envoyer une invitation pour rejoindre une organisation de type SCO en tant que ADMIN, en renseignant un **UAI**, un **NOM** et un **PRÉNOM**",
],
tags: ['api', 'invitations', 'SCO'],
tags: ['team', 'api', 'invitations', 'SCO'],
},
},
{
Expand All @@ -45,7 +46,31 @@ export const organizationInvitationRoutes = [
notes: [
"- Cette route permet de récupérer les détails d'une invitation selon un **id d'invitation** et un **code**\n",
],
tags: ['api', 'invitations'],
tags: ['team', 'api', 'invitations'],
},
},
{
method: 'DELETE',
path: '/api/organizations/{id}/invitations/{organizationInvitationId}',
config: {
pre: [
{
method: (request, h) => securityPreHandlers.checkUserIsAdminInOrganization(request, h),
assign: 'isAdminInOrganization',
},
],
validate: {
params: Joi.object({
id: identifiersType.organizationId,
organizationInvitationId: identifiersType.organizationInvitationId,
}),
},
handler: (request, h) => organizationInvitationController.cancelOrganizationInvitation(request, h),
tags: ['team', 'api', 'invitations', 'cancel'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés en tant qu'admin d'une organisation**\n" +
"- Elle permet à l'administrateur de l'organisation d'annuler une invitation envoyée mais non acceptée encore.",
],
},
},
];
2 changes: 2 additions & 0 deletions api/src/team/application/routes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { certificationCenterInvitationAdminRoutes } from './certification-center-invitation/certification-center-invitation.admin.route.js';
import { certificationCenterInvitationRoutes } from './certification-center-invitation/certification-center-invitation.route.js';
import { organizationInvitationAdminRoutes } from './organization-invitations/organization-invitation.admin.route.js';
import { organizationInvitationRoutes } from './organization-invitations/organization-invitation.route.js';
import { prescriberInformationsRoute } from './prescriber-informations.route.js';

Expand All @@ -9,6 +10,7 @@ const register = async function (server) {
...certificationCenterInvitationAdminRoutes,
...prescriberInformationsRoute,
...organizationInvitationRoutes,
...organizationInvitationAdminRoutes,
]);
};

Expand Down
21 changes: 18 additions & 3 deletions api/src/team/domain/errors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DomainError } from '../../shared/domain/errors.js';
import { DomainError } from '../../../lib/domain/errors.js';

export class UncancellableCertificationCenterInvitationError extends DomainError {
class UncancellableCertificationCenterInvitationError extends DomainError {
constructor(
message = "L'invitation à ce centre de certification ne peut pas être annulée.",
code = 'UNCANCELLABLE_CERTIFICATION_CENTER_INVITATION_CODE',
Expand All @@ -9,4 +9,19 @@ export class UncancellableCertificationCenterInvitationError extends DomainError
}
}

export class MembershipNotFound extends DomainError {}
class UncancellableOrganizationInvitationError extends DomainError {
constructor(
message = "L'invitation à cette organisation ne peut pas être annulée.",
code = 'UNCANCELLABLE_ORGANIZATION_INVITATION_CODE',
) {
super(message, code);
}
}

class MembershipNotFound extends DomainError {}

export {
MembershipNotFound,
UncancellableCertificationCenterInvitationError,
UncancellableOrganizationInvitationError,
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UncancellableOrganizationInvitationError } from '../../domain/errors.js';
import { UncancellableOrganizationInvitationError } from '../errors.js';

const cancelOrganizationInvitation = async function ({ organizationInvitationId, organizationInvitationRepository }) {
const foundOrganizationInvitation = await organizationInvitationRepository.get(organizationInvitationId);
Expand Down

This file was deleted.

Loading

0 comments on commit bd2f2ec

Please sign in to comment.