From 22dc22588911ca55060adc28de37a4dd9e46eb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannick=20Fran=C3=A7ois?= Date: Mon, 4 Nov 2024 11:33:06 +0100 Subject: [PATCH] :recycle: refactor: move use-orga-settings routes & co to `src` --- .../application/user-orga-settings/index.js | 51 ---- api/lib/routes.js | 2 - api/src/shared/application/error-manager.js | 245 ++++++++---------- api/src/shared/domain/errors.js | 7 - api/src/team/application/routes.js | 2 + .../user-orga-settings.controller.js} | 4 +- .../application/user-orga-settings.route.js | 46 ++++ api/src/team/domain/errors.js | 7 + ...e-or-update-user-orga-settings.usecase.js} | 2 +- .../team/domain/usecases/get-prescriber.js | 2 +- .../jsonapi/user-orga-settings.serializer.js} | 0 .../application/error-manager_test.js | 9 +- .../user-orga-settings.controller.test.js} | 0 .../user-orga-settings.controller.test.js} | 4 +- .../user-orga-settings.route.test.js} | 18 +- ...update-user-orga-settings.usecase.test.js} | 6 +- .../domain/usecases/get-prescriber_test.js | 2 +- .../user-orga-settings.serializer.test.js} | 6 +- 18 files changed, 199 insertions(+), 214 deletions(-) delete mode 100644 api/lib/application/user-orga-settings/index.js rename api/{lib/application/user-orga-settings/user-orga-settings-controller.js => src/team/application/user-orga-settings.controller.js} (72%) create mode 100644 api/src/team/application/user-orga-settings.route.js rename api/{lib/domain/usecases/create-or-update-user-orga-settings.js => src/team/domain/usecases/create-or-update-user-orga-settings.usecase.js} (86%) rename api/{lib/infrastructure/serializers/jsonapi/user-orga-settings-serializer.js => src/team/infrastructure/serializers/jsonapi/user-orga-settings.serializer.js} (100%) rename api/tests/{acceptance/application/user-orga-settings/user-orga-settings-controller_test.js => team/acceptance/application/user-orga-settings.controller.test.js} (100%) rename api/tests/{unit/application/user-orga-settings/user-orga-settings-controller_test.js => team/unit/application/user-orga-settings.controller.test.js} (92%) rename api/tests/{unit/application/user-orga-settings/index_test.js => team/unit/application/user-orga-settings.route.test.js} (84%) rename api/tests/{unit/domain/usecases/create-or-update-user-orga-settings_test.js => team/unit/domain/usecases/create-or-update-user-orga-settings.usecase.test.js} (83%) rename api/tests/{unit/infrastructure/serializers/jsonapi/user-orga-settings-serializer_test.js => team/unit/infrastructure/serializers/jsonapi/user-orga-settings.serializer.test.js} (94%) diff --git a/api/lib/application/user-orga-settings/index.js b/api/lib/application/user-orga-settings/index.js deleted file mode 100644 index b9a1baa5be7..00000000000 --- a/api/lib/application/user-orga-settings/index.js +++ /dev/null @@ -1,51 +0,0 @@ -import Joi from 'joi'; - -import { securityPreHandlers } from '../../../src/shared/application/security-pre-handlers.js'; -import { identifiersType } from '../../../src/shared/domain/types/identifiers-type.js'; -import { userOrgaSettingsController } from './user-orga-settings-controller.js'; - -const register = async function (server) { - server.route([ - { - method: 'PUT', - path: '/api/user-orga-settings/{id}', - config: { - pre: [ - { - method: securityPreHandlers.checkRequestedUserIsAuthenticatedUser, - assign: 'requestedUserIsAuthenticatedUser', - }, - ], - validate: { - options: { - allowUnknown: true, - }, - params: Joi.object({ - id: identifiersType.userId, - }), - payload: Joi.object({ - data: { - relationships: { - organization: { - data: { - id: identifiersType.organizationId, - }, - }, - }, - }, - }), - }, - handler: userOrgaSettingsController.createOrUpdate, - notes: [ - '- **Cette route est restreinte aux utilisateurs authentifiés**\n' + - '- Création ou Mise à jour des paramètres utilisateurs liés à Pix Orga, permet notamment d’enregistrer les préférences d’un prescripteur vis à vis de son espace Orga.\n' + - '- L’id en paramètre doit correspondre à celui de l’utilisateur authentifié', - ], - tags: ['api', 'user-orga-settings'], - }, - }, - ]); -}; - -const name = 'user-orga-settings-api'; -export { name, register }; diff --git a/api/lib/routes.js b/api/lib/routes.js index 888a92bbdcd..e75b06f81f5 100644 --- a/api/lib/routes.js +++ b/api/lib/routes.js @@ -14,7 +14,6 @@ import * as scoOrganizationLearners from './application/sco-organization-learner import * as sessions from './application/sessions/index.js'; import * as tags from './application/tags/index.js'; import * as targetProfiles from './application/target-profiles/index.js'; -import * as userOrgaSettings from './application/user-orga-settings/index.js'; import * as users from './application/users/index.js'; const routes = [ @@ -34,7 +33,6 @@ const routes = [ tags, targetProfiles, frameworks, - userOrgaSettings, users, ]; diff --git a/api/src/shared/application/error-manager.js b/api/src/shared/application/error-manager.js index f5fd0c25a00..fdf0481ade0 100644 --- a/api/src/shared/application/error-manager.js +++ b/api/src/shared/application/error-manager.js @@ -10,23 +10,11 @@ import { ArchivedCampaignError, DeletedCampaignError } from '../../prescription/ import { CampaignParticipationDeletedError } from '../../prescription/campaign-participation/domain/errors.js'; import { AggregateImportError, SiecleXmlImportError } from '../../prescription/learner-management/domain/errors.js'; import { OrganizationCantGetPlacesStatisticsError } from '../../prescription/organization-place/domain/errors.js'; -import { AlreadyAcceptedOrCancelledInvitationError } from '../../team/domain/errors.js'; -import * as DomainErrors from '../domain/errors.js'; import { - AutonomousCourseRequiresATargetProfileWithSimplifiedAccessError, - CertificationCenterPilotFeaturesConflictError, - EntityValidationError, - OidcError, - TargetProfileRequiresToBeLinkedToAutonomousCourseOrganization, - UserAlreadyExistsWithAuthenticationMethodError, - UserAlreadyLinkedToCandidateInSessionError, - UserCouldNotBeReconciledError, - UserHasAlreadyLeftSCO, - UserIsBlocked, - UserIsTemporaryBlocked, - UserNotAuthorizedToAccessEntityError, - UserNotFoundError, -} from '../domain/errors.js'; + AlreadyAcceptedOrCancelledInvitationError, + UserNotMemberOfOrganizationError, +} from '../../team/domain/errors.js'; +import * as SharedDomainErrors from '../domain/errors.js'; import * as errorSerializer from '../infrastructure/serializers/jsonapi/error-serializer.js'; import { extractLocaleFromRequest } from '../infrastructure/utils/request-response-utils.js'; import { domainErrorMapper } from './domain-error-mapper.js'; @@ -89,93 +77,90 @@ function _mapToHttpError(error) { if (error instanceof HttpErrors.BaseHttpError) { return error; } - if (error instanceof DomainErrors.CertificationCandidateNotFoundError) { + if (error instanceof SharedDomainErrors.CertificationCandidateNotFoundError) { return new HttpErrors.NotFoundError(error.message, error.code); } - if (error instanceof DomainErrors.NotFoundError) { + if (error instanceof SharedDomainErrors.NotFoundError) { return new HttpErrors.NotFoundError(error.message); } - if (error instanceof DomainErrors.ForbiddenAccess) { + if (error instanceof SharedDomainErrors.ForbiddenAccess) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.CsvImportError) { + if (error instanceof SharedDomainErrors.CsvImportError) { return new HttpErrors.PreconditionFailedError(error.message, error.code, error.meta); } - if (error instanceof UserNotAuthorizedToAccessEntityError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToAccessEntityError) { return new HttpErrors.ForbiddenError('Utilisateur non autorisé à accéder à la ressource'); } - if (error instanceof UserIsTemporaryBlocked) { + if (error instanceof SharedDomainErrors.UserIsTemporaryBlocked) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof UserHasAlreadyLeftSCO) { + if (error instanceof SharedDomainErrors.UserHasAlreadyLeftSCO) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof UserAlreadyExistsWithAuthenticationMethodError) { + if (error instanceof SharedDomainErrors.UserAlreadyExistsWithAuthenticationMethodError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof UserNotFoundError) { + if (error instanceof SharedDomainErrors.UserNotFoundError) { return new HttpErrors.NotFoundError(error.message); } - if (error instanceof UserNotAuthorizedToAccessEntityError) { - return new HttpErrors.ForbiddenError('Utilisateur non autorisé à accéder à la ressource'); - } - if (error instanceof UserIsBlocked) { + if (error instanceof SharedDomainErrors.UserIsBlocked) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof UserAlreadyLinkedToCandidateInSessionError) { + if (error instanceof SharedDomainErrors.UserAlreadyLinkedToCandidateInSessionError) { return new HttpErrors.ForbiddenError("L'utilisateur est déjà lié à un candidat dans cette session."); } - if (error instanceof DomainErrors.AlreadyRegisteredEmailError) { + if (error instanceof SharedDomainErrors.AlreadyRegisteredEmailError) { return new HttpErrors.BadRequestError(error.message, error.code); } - if (error instanceof DomainErrors.AssessmentEndedError) { + if (error instanceof SharedDomainErrors.AssessmentEndedError) { return new HttpErrors.BaseHttpError(error.message); } - if (error instanceof DomainErrors.CertificationAttestationGenerationError) { + if (error instanceof SharedDomainErrors.CertificationAttestationGenerationError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof UserCouldNotBeReconciledError) { + if (error instanceof SharedDomainErrors.UserCouldNotBeReconciledError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof DomainErrors.EmailModificationDemandNotFoundOrExpiredError) { + if (error instanceof SharedDomainErrors.EmailModificationDemandNotFoundOrExpiredError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.InvalidExternalUserTokenError) { + if (error instanceof SharedDomainErrors.InvalidExternalUserTokenError) { return new HttpErrors.UnauthorizedError(error.message); } - if (error instanceof DomainErrors.InvalidResultRecipientTokenError) { + if (error instanceof SharedDomainErrors.InvalidResultRecipientTokenError) { return new HttpErrors.UnauthorizedError(error.message); } - if (error instanceof DomainErrors.InvalidTemporaryKeyError) { + if (error instanceof SharedDomainErrors.InvalidTemporaryKeyError) { return new HttpErrors.UnauthorizedError(error.message); } - if (error instanceof DomainErrors.InvalidVerificationCodeError) { + if (error instanceof SharedDomainErrors.InvalidVerificationCodeError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.LocaleFormatError) { + if (error instanceof SharedDomainErrors.LocaleFormatError) { return new HttpErrors.BadRequestError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.LanguageNotSupportedError) { + if (error instanceof SharedDomainErrors.LanguageNotSupportedError) { return new HttpErrors.BadRequestError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.LocaleNotSupportedError) { + if (error instanceof SharedDomainErrors.LocaleNotSupportedError) { return new HttpErrors.BadRequestError(error.message, error.code, error.meta); } if (error instanceof AdminMemberError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code); } - if (error instanceof DomainErrors.NoCertificationAttestationForDivisionError) { + if (error instanceof SharedDomainErrors.NoCertificationAttestationForDivisionError) { return new HttpErrors.BadRequestError(error.message); } if (error instanceof OrganizationCantGetPlacesStatisticsError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof AutonomousCourseRequiresATargetProfileWithSimplifiedAccessError) { + if (error instanceof SharedDomainErrors.AutonomousCourseRequiresATargetProfileWithSimplifiedAccessError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof TargetProfileRequiresToBeLinkedToAutonomousCourseOrganization) { + if (error instanceof SharedDomainErrors.TargetProfileRequiresToBeLinkedToAutonomousCourseOrganization) { return new HttpErrors.BadRequestError(error.message); } if (error instanceof SiecleXmlImportError) { @@ -193,11 +178,11 @@ function _mapToHttpError(error) { return new HttpErrors.UnprocessableEntityError(error.message, error.code); } - if (error instanceof OidcError) { + if (error instanceof SharedDomainErrors.OidcError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.InvalidInputDataError) { + if (error instanceof SharedDomainErrors.InvalidInputDataError) { return new HttpErrors.PreconditionFailedError(error.message, error.code, error.meta); } @@ -209,282 +194,282 @@ function _mapToHttpError(error) { return new HttpErrors.BadRequestError(error.message, error.code); } - if (error instanceof DomainErrors.SendingEmailToRefererError) { + if (error instanceof SharedDomainErrors.SendingEmailToRefererError) { return new HttpErrors.ServiceUnavailableError(error.message); } - if (error instanceof DomainErrors.SendingEmailError) { + if (error instanceof SharedDomainErrors.SendingEmailError) { return new HttpErrors.ServiceUnavailableError(error.message); } - if (error instanceof DomainErrors.ApplicationWithInvalidClientIdError) { + if (error instanceof SharedDomainErrors.ApplicationWithInvalidClientIdError) { return new HttpErrors.UnauthorizedError('The client ID is invalid.'); } - if (error instanceof DomainErrors.CertificationCandidatesError) { + if (error instanceof SharedDomainErrors.CertificationCandidatesError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.AuthenticationMethodAlreadyExistsError) { + if (error instanceof SharedDomainErrors.AuthenticationMethodAlreadyExistsError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof DomainErrors.ApplicationWithInvalidClientSecretError) { + if (error instanceof SharedDomainErrors.ApplicationWithInvalidClientSecretError) { return new HttpErrors.UnauthorizedError('The client secret is invalid.'); } - if (error instanceof DomainErrors.MissingAttributesError) { + if (error instanceof SharedDomainErrors.MissingAttributesError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof DomainErrors.SendingEmailToResultRecipientError) { + if (error instanceof SharedDomainErrors.SendingEmailToResultRecipientError) { return new HttpErrors.ServiceUnavailableError(error.message); } - if (error instanceof DomainErrors.InvalidPasswordForUpdateEmailError) { + if (error instanceof SharedDomainErrors.InvalidPasswordForUpdateEmailError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToUpdateEmailError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToUpdateEmailError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToUpdatePasswordError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToUpdatePasswordError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof CertificationCenterPilotFeaturesConflictError) { + if (error instanceof SharedDomainErrors.CertificationCenterPilotFeaturesConflictError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.AlreadyExistingEntityError) { + if (error instanceof SharedDomainErrors.AlreadyExistingEntityError) { return new HttpErrors.PreconditionFailedError(error.message, error.code, error.meta); } if (error instanceof UnableToAttachChildOrganizationToParentOrganizationError) { return new HttpErrors.ConflictError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.AccountRecoveryDemandExpired) { + if (error instanceof SharedDomainErrors.AccountRecoveryDemandExpired) { return new HttpErrors.UnauthorizedError(error.message); } - if (error instanceof DomainErrors.AccountRecoveryUserAlreadyConfirmEmail) { + if (error instanceof SharedDomainErrors.AccountRecoveryUserAlreadyConfirmEmail) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.AlreadyRatedAssessmentError) { + if (error instanceof SharedDomainErrors.AlreadyRatedAssessmentError) { return new HttpErrors.PreconditionFailedError('Assessment is already rated.'); } - if (error instanceof DomainErrors.NotEnoughDaysPassedBeforeResetCampaignParticipationError) { + if (error instanceof SharedDomainErrors.NotEnoughDaysPassedBeforeResetCampaignParticipationError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.NoCampaignParticipationForUserAndCampaign) { + if (error instanceof SharedDomainErrors.NoCampaignParticipationForUserAndCampaign) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.OrganizationLearnerDisabledError) { + if (error instanceof SharedDomainErrors.OrganizationLearnerDisabledError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.NoOrganizationToAttach) { + if (error instanceof SharedDomainErrors.NoOrganizationToAttach) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.ChallengeAlreadyAnsweredError) { + if (error instanceof SharedDomainErrors.ChallengeAlreadyAnsweredError) { return new HttpErrors.ConflictError('This challenge has already been answered.'); } - if (error instanceof DomainErrors.ChallengeNotAskedError) { + if (error instanceof SharedDomainErrors.ChallengeNotAskedError) { return new HttpErrors.ConflictError('This challenge has not been asked to the user.'); } - if (error instanceof DomainErrors.NotFoundError) { + if (error instanceof SharedDomainErrors.NotFoundError) { return new HttpErrors.NotFoundError(error.message, error.code); } - if (error instanceof DomainErrors.DeletedError) { + if (error instanceof SharedDomainErrors.DeletedError) { return new HttpErrors.ConflictError(error.message, error.code); } - if (error instanceof DomainErrors.CampaignCodeError) { + if (error instanceof SharedDomainErrors.CampaignCodeError) { return new HttpErrors.NotFoundError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToUpdateResourceError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToUpdateResourceError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToCreateCampaignError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToCreateCampaignError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToGenerateUsernamePasswordError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToGenerateUsernamePasswordError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToRemoveAuthenticationMethod) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToRemoveAuthenticationMethod) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.CandidateAlreadyLinkedToUserError) { + if (error instanceof SharedDomainErrors.CandidateAlreadyLinkedToUserError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.CertificationCandidateByPersonalInfoNotFoundError) { + if (error instanceof SharedDomainErrors.CertificationCandidateByPersonalInfoNotFoundError) { return new HttpErrors.NotFoundError( "Aucun candidat de certification ne correspond aux informations d'identité fournies.", ); } - if (error instanceof DomainErrors.CertificationCandidateByPersonalInfoTooManyMatchesError) { + if (error instanceof SharedDomainErrors.CertificationCandidateByPersonalInfoTooManyMatchesError) { return new HttpErrors.ConflictError( "Plus d'un candidat de certification correspondent aux informations d'identité fournies.", ); } - if (error instanceof DomainErrors.CertificationCandidatePersonalInfoFieldMissingError) { + if (error instanceof SharedDomainErrors.CertificationCandidatePersonalInfoFieldMissingError) { return new HttpErrors.BadRequestError("Un ou plusieurs champs d'informations d'identité sont manquants."); } - if (error instanceof DomainErrors.CertificationCandidatePersonalInfoWrongFormat) { + if (error instanceof SharedDomainErrors.CertificationCandidatePersonalInfoWrongFormat) { return new HttpErrors.BadRequestError("Un ou plusieurs champs d'informations d'identité sont au mauvais format."); } - if (error instanceof DomainErrors.CancelledInvitationError) { + if (error instanceof SharedDomainErrors.CancelledInvitationError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.CertificationCenterMembershipCreationError) { + if (error instanceof SharedDomainErrors.CertificationCenterMembershipCreationError) { return new HttpErrors.BadRequestError("Le membre ou le centre de certification n'existe pas."); } - if (error instanceof DomainErrors.InvalidExternalAPIResponseError) { + if (error instanceof SharedDomainErrors.InvalidExternalAPIResponseError) { return new HttpErrors.ServiceUnavailableError(error.message); } - if (error instanceof DomainErrors.UnexpectedUserAccountError) { + if (error instanceof SharedDomainErrors.UnexpectedUserAccountError) { return new HttpErrors.ConflictError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.AlreadyExistingEntityError) { + if (error instanceof SharedDomainErrors.AlreadyExistingEntityError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.AlreadyExistingMembershipError) { + if (error instanceof SharedDomainErrors.AlreadyExistingMembershipError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.AlreadyExistingInvitationError) { + if (error instanceof SharedDomainErrors.AlreadyExistingInvitationError) { return new HttpErrors.PreconditionFailedError(error.message); } if (error instanceof AlreadyAcceptedOrCancelledInvitationError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.AlreadyExistingCampaignParticipationError) { + if (error instanceof SharedDomainErrors.AlreadyExistingCampaignParticipationError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.AlreadySharedCampaignParticipationError) { + if (error instanceof SharedDomainErrors.AlreadySharedCampaignParticipationError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.MembershipCreationError) { + if (error instanceof SharedDomainErrors.MembershipCreationError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.MembershipUpdateError) { + if (error instanceof SharedDomainErrors.MembershipUpdateError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.ObjectValidationError) { + if (error instanceof SharedDomainErrors.ObjectValidationError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof DomainErrors.OrganizationNotFoundError) { + if (error instanceof SharedDomainErrors.OrganizationNotFoundError) { return new HttpErrors.NotFoundError(error.message); } - if (error instanceof DomainErrors.OrganizationWithoutEmailError) { + if (error instanceof SharedDomainErrors.OrganizationWithoutEmailError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.ManyOrganizationsFoundError) { + if (error instanceof SharedDomainErrors.ManyOrganizationsFoundError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.FileValidationError) { + if (error instanceof SharedDomainErrors.FileValidationError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.OrganizationLearnersCouldNotBeSavedError) { + if (error instanceof SharedDomainErrors.OrganizationLearnersCouldNotBeSavedError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.OrganizationLearnersConstraintError) { + if (error instanceof SharedDomainErrors.OrganizationLearnersConstraintError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.AssessmentNotCompletedError) { + if (error instanceof SharedDomainErrors.AssessmentNotCompletedError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.UserNotAuthorizedToCertifyError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToCertifyError) { return new HttpErrors.ForbiddenError('The user cannot be certified.'); } - if (error instanceof DomainErrors.ApplicationScopeNotAllowedError) { + if (error instanceof SharedDomainErrors.ApplicationScopeNotAllowedError) { return new HttpErrors.ForbiddenError('The scope is not allowed.'); } - if (error instanceof DomainErrors.UserNotAuthorizedToGetCampaignResultsError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToGetCampaignResultsError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.AlreadyRegisteredEmailAndUsernameError) { + if (error instanceof SharedDomainErrors.AlreadyRegisteredEmailAndUsernameError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.AlreadyRegisteredUsernameError) { + if (error instanceof SharedDomainErrors.AlreadyRegisteredUsernameError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.WrongDateFormatError) { + if (error instanceof SharedDomainErrors.WrongDateFormatError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.OrganizationLearnerAlreadyLinkedToUserError) { + if (error instanceof SharedDomainErrors.OrganizationLearnerAlreadyLinkedToUserError) { return new HttpErrors.ConflictError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.OrganizationLearnerAlreadyLinkedToInvalidUserError) { + if (error instanceof SharedDomainErrors.OrganizationLearnerAlreadyLinkedToInvalidUserError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.MatchingReconciledStudentNotFoundError) { + if (error instanceof SharedDomainErrors.MatchingReconciledStudentNotFoundError) { return new HttpErrors.BadRequestError(error.message, error.code); } - if (error instanceof DomainErrors.UserNotAuthorizedToCreateResourceError) { + if (error instanceof SharedDomainErrors.UserNotAuthorizedToCreateResourceError) { return new HttpErrors.ForbiddenError(error.message); } - if (error instanceof DomainErrors.UserOrgaSettingsCreationError) { + if (error instanceof SharedDomainErrors.UserOrgaSettingsCreationError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.UserNotMemberOfOrganizationError) { + if (error instanceof UserNotMemberOfOrganizationError) { return new HttpErrors.UnprocessableEntityError(error.message); } - if (error instanceof DomainErrors.TargetProfileInvalidError) { + if (error instanceof SharedDomainErrors.TargetProfileInvalidError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.NoStagesForCampaign) { + if (error instanceof SharedDomainErrors.NoStagesForCampaign) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.CampaignTypeError) { + if (error instanceof SharedDomainErrors.CampaignTypeError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.InvalidMembershipOrganizationRoleError) { + if (error instanceof SharedDomainErrors.InvalidMembershipOrganizationRoleError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.OidcMissingFieldsError) { + if (error instanceof SharedDomainErrors.OidcMissingFieldsError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.InvalidIdentityProviderError) { + if (error instanceof SharedDomainErrors.InvalidIdentityProviderError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.YamlParsingError) { + if (error instanceof SharedDomainErrors.YamlParsingError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.MultipleOrganizationLearnersWithDifferentNationalStudentIdError) { + if (error instanceof SharedDomainErrors.MultipleOrganizationLearnersWithDifferentNationalStudentIdError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.UserShouldNotBeReconciledOnAnotherAccountError) { + if (error instanceof SharedDomainErrors.UserShouldNotBeReconciledOnAnotherAccountError) { return new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta); } - if (error instanceof DomainErrors.CandidateNotAuthorizedToJoinSessionError) { + if (error instanceof SharedDomainErrors.CandidateNotAuthorizedToJoinSessionError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.CandidateNotAuthorizedToResumeCertificationTestError) { + if (error instanceof SharedDomainErrors.CandidateNotAuthorizedToResumeCertificationTestError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.CertificationCandidateOnFinalizedSessionError) { + if (error instanceof SharedDomainErrors.CertificationCandidateOnFinalizedSessionError) { return new HttpErrors.ForbiddenError(error.message, error.code); } - if (error instanceof DomainErrors.OrganizationLearnerCannotBeDissociatedError) { + if (error instanceof SharedDomainErrors.OrganizationLearnerCannotBeDissociatedError) { return new HttpErrors.PreconditionFailedError(error.message); } - if (error instanceof DomainErrors.CertificationEndedByFinalizationError) { + if (error instanceof SharedDomainErrors.CertificationEndedByFinalizationError) { return new HttpErrors.ConflictError(error.message); } - if (error instanceof DomainErrors.InvalidJuryLevelError) { + if (error instanceof SharedDomainErrors.InvalidJuryLevelError) { return new HttpErrors.BadRequestError(error.message); } - if (error instanceof DomainErrors.SendingEmailToInvalidDomainError) { + if (error instanceof SharedDomainErrors.SendingEmailToInvalidDomainError) { return new HttpErrors.BadRequestError(error.message, 'SENDING_EMAIL_TO_INVALID_DOMAIN'); } - if (error instanceof DomainErrors.SendingEmailToInvalidEmailAddressError) { + if (error instanceof SharedDomainErrors.SendingEmailToInvalidEmailAddressError) { return new HttpErrors.BadRequestError(error.message, 'SENDING_EMAIL_TO_INVALID_EMAIL_ADDRESS', error.meta); } @@ -492,7 +477,7 @@ function _mapToHttpError(error) { } function handle(request, h, error) { - if (error instanceof EntityValidationError) { + if (error instanceof SharedDomainErrors.EntityValidationError) { const locale = extractLocaleFromRequest(request).split('-')[0]; const jsonApiError = new JSONAPIError( diff --git a/api/src/shared/domain/errors.js b/api/src/shared/domain/errors.js index 471eb646d01..b6c7b256dd7 100644 --- a/api/src/shared/domain/errors.js +++ b/api/src/shared/domain/errors.js @@ -900,12 +900,6 @@ class UserOrgaSettingsCreationError extends DomainError { } } -class UserNotMemberOfOrganizationError extends DomainError { - constructor(message = "L'utilisateur n'est pas membre de l'organisation.") { - super(message); - } -} - class FileValidationError extends DomainError { constructor(code, meta) { super('An error occurred, file is invalid'); @@ -1198,7 +1192,6 @@ export { UserNotAuthorizedToUpdatePasswordError, UserNotAuthorizedToUpdateResourceError, UserNotFoundError, - UserNotMemberOfOrganizationError, UserOrgaSettingsCreationError, UserShouldNotBeReconciledOnAnotherAccountError, WrongDateFormatError, diff --git a/api/src/team/application/routes.js b/api/src/team/application/routes.js index 54af524cd71..62b4cb25375 100644 --- a/api/src/team/application/routes.js +++ b/api/src/team/application/routes.js @@ -7,6 +7,7 @@ import { membershipRoutes } from './membership/membership.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'; +import { userOrgaSettingsRoute } from './user-orga-settings.route.js'; const register = async function (server) { server.route([ @@ -16,6 +17,7 @@ const register = async function (server) { ...certificationCenterMembershipRoute, ...membershipAdminRoutes, ...membershipRoutes, + ...userOrgaSettingsRoute, ...prescriberInformationsRoute, ...organizationInvitationRoutes, ...organizationInvitationAdminRoutes, diff --git a/api/lib/application/user-orga-settings/user-orga-settings-controller.js b/api/src/team/application/user-orga-settings.controller.js similarity index 72% rename from api/lib/application/user-orga-settings/user-orga-settings-controller.js rename to api/src/team/application/user-orga-settings.controller.js index 65a289caeba..47545d02820 100644 --- a/api/lib/application/user-orga-settings/user-orga-settings-controller.js +++ b/api/src/team/application/user-orga-settings.controller.js @@ -1,5 +1,5 @@ -import { usecases } from '../../domain/usecases/index.js'; -import * as userOrgaSettingsSerializer from '../../infrastructure/serializers/jsonapi/user-orga-settings-serializer.js'; +import { usecases } from '../domain/usecases/index.js'; +import * as userOrgaSettingsSerializer from '../infrastructure/serializers/jsonapi/user-orga-settings.serializer.js'; const createOrUpdate = async function (request, h, dependencies = { userOrgaSettingsSerializer }) { const userId = request.params.id; diff --git a/api/src/team/application/user-orga-settings.route.js b/api/src/team/application/user-orga-settings.route.js new file mode 100644 index 00000000000..e12e54e417f --- /dev/null +++ b/api/src/team/application/user-orga-settings.route.js @@ -0,0 +1,46 @@ +import Joi from 'joi'; + +import { securityPreHandlers } from '../../../src/shared/application/security-pre-handlers.js'; +import { identifiersType } from '../../../src/shared/domain/types/identifiers-type.js'; +import { userOrgaSettingsController } from './user-orga-settings.controller.js'; + +export const userOrgaSettingsRoute = [ + { + method: 'PUT', + path: '/api/user-orga-settings/{id}', + config: { + pre: [ + { + method: securityPreHandlers.checkRequestedUserIsAuthenticatedUser, + assign: 'requestedUserIsAuthenticatedUser', + }, + ], + validate: { + options: { + allowUnknown: true, + }, + params: Joi.object({ + id: identifiersType.userId, + }), + payload: Joi.object({ + data: { + relationships: { + organization: { + data: { + id: identifiersType.organizationId, + }, + }, + }, + }, + }), + }, + handler: (request, h) => userOrgaSettingsController.createOrUpdate(request, h), + notes: [ + '- **Cette route est restreinte aux utilisateurs authentifiés**\n' + + '- Création ou Mise à jour des paramètres utilisateurs liés à Pix Orga, permet notamment d’enregistrer les préférences d’un prescripteur vis à vis de son espace Orga.\n' + + '- L’id en paramètre doit correspondre à celui de l’utilisateur authentifié', + ], + tags: ['api', 'user-orga-settings'], + }, + }, +]; diff --git a/api/src/team/domain/errors.js b/api/src/team/domain/errors.js index f14b6ecd1da..aa5a9bef645 100644 --- a/api/src/team/domain/errors.js +++ b/api/src/team/domain/errors.js @@ -30,6 +30,12 @@ class UncancellableOrganizationInvitationError extends DomainError { } } +class UserNotMemberOfOrganizationError extends DomainError { + constructor(message = "L'utilisateur n'est pas membre de l'organisation.") { + super(message); + } +} + class MembershipNotFound extends DomainError {} class OrganizationArchivedError extends DomainError { @@ -45,4 +51,5 @@ export { OrganizationArchivedError, UncancellableCertificationCenterInvitationError, UncancellableOrganizationInvitationError, + UserNotMemberOfOrganizationError, }; diff --git a/api/lib/domain/usecases/create-or-update-user-orga-settings.js b/api/src/team/domain/usecases/create-or-update-user-orga-settings.usecase.js similarity index 86% rename from api/lib/domain/usecases/create-or-update-user-orga-settings.js rename to api/src/team/domain/usecases/create-or-update-user-orga-settings.usecase.js index 171432a862d..79813d7b898 100644 --- a/api/lib/domain/usecases/create-or-update-user-orga-settings.js +++ b/api/src/team/domain/usecases/create-or-update-user-orga-settings.usecase.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { UserNotMemberOfOrganizationError } from '../../../src/shared/domain/errors.js'; +import { UserNotMemberOfOrganizationError } from '../errors.js'; const createOrUpdateUserOrgaSettings = async function ({ userId, diff --git a/api/src/team/domain/usecases/get-prescriber.js b/api/src/team/domain/usecases/get-prescriber.js index c26d01ccabf..2d8d4586f25 100644 --- a/api/src/team/domain/usecases/get-prescriber.js +++ b/api/src/team/domain/usecases/get-prescriber.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { UserNotMemberOfOrganizationError } from '../../../shared/domain/errors.js'; +import { UserNotMemberOfOrganizationError } from '../errors.js'; /** * @param {{ diff --git a/api/lib/infrastructure/serializers/jsonapi/user-orga-settings-serializer.js b/api/src/team/infrastructure/serializers/jsonapi/user-orga-settings.serializer.js similarity index 100% rename from api/lib/infrastructure/serializers/jsonapi/user-orga-settings-serializer.js rename to api/src/team/infrastructure/serializers/jsonapi/user-orga-settings.serializer.js diff --git a/api/tests/integration/application/error-manager_test.js b/api/tests/integration/application/error-manager_test.js index 9dfed4490dd..ef6d7a35e4c 100644 --- a/api/tests/integration/application/error-manager_test.js +++ b/api/tests/integration/application/error-manager_test.js @@ -7,7 +7,10 @@ import { } from '../../../src/identity-access-management/domain/errors.js'; import { CampaignParticipationDeletedError } from '../../../src/prescription/campaign-participation/domain/errors.js'; import * as DomainErrors from '../../../src/shared/domain/errors.js'; -import { AlreadyAcceptedOrCancelledInvitationError } from '../../../src/team/domain/errors.js'; +import { + AlreadyAcceptedOrCancelledInvitationError, + UserNotMemberOfOrganizationError, +} from '../../../src/team/domain/errors.js'; import { expect, HttpTestServer, sinon } from '../../test-helper.js'; describe('Integration | API | Controller Error', function () { @@ -491,9 +494,7 @@ describe('Integration | API | Controller Error', function () { }); it('responds Unprocessable Entity when a UserNotMemberOfOrganizationError error occurs', async function () { - routeHandler.throws( - new DomainErrors.UserNotMemberOfOrganizationError("L'utilisateur n'est pas membre de l'organisation."), - ); + routeHandler.throws(new UserNotMemberOfOrganizationError("L'utilisateur n'est pas membre de l'organisation.")); const response = await server.requestObject(request); expect(response.statusCode).to.equal(UNPROCESSABLE_ENTITY_ERROR); diff --git a/api/tests/acceptance/application/user-orga-settings/user-orga-settings-controller_test.js b/api/tests/team/acceptance/application/user-orga-settings.controller.test.js similarity index 100% rename from api/tests/acceptance/application/user-orga-settings/user-orga-settings-controller_test.js rename to api/tests/team/acceptance/application/user-orga-settings.controller.test.js diff --git a/api/tests/unit/application/user-orga-settings/user-orga-settings-controller_test.js b/api/tests/team/unit/application/user-orga-settings.controller.test.js similarity index 92% rename from api/tests/unit/application/user-orga-settings/user-orga-settings-controller_test.js rename to api/tests/team/unit/application/user-orga-settings.controller.test.js index 5e286f1821e..6ae3c2f2f5b 100644 --- a/api/tests/unit/application/user-orga-settings/user-orga-settings-controller_test.js +++ b/api/tests/team/unit/application/user-orga-settings.controller.test.js @@ -1,5 +1,5 @@ -import { userOrgaSettingsController } from '../../../../lib/application/user-orga-settings/user-orga-settings-controller.js'; -import { usecases } from '../../../../lib/domain/usecases/index.js'; +import { userOrgaSettingsController } from '../../../../src/team/application/user-orga-settings.controller.js'; +import { usecases } from '../../../../src/team/domain/usecases/index.js'; import { expect, hFake, sinon } from '../../../test-helper.js'; describe('Unit | Controller | user-orga-settings-controller', function () { diff --git a/api/tests/unit/application/user-orga-settings/index_test.js b/api/tests/team/unit/application/user-orga-settings.route.test.js similarity index 84% rename from api/tests/unit/application/user-orga-settings/index_test.js rename to api/tests/team/unit/application/user-orga-settings.route.test.js index d973601c4d7..8cfbf4e3a07 100644 --- a/api/tests/unit/application/user-orga-settings/index_test.js +++ b/api/tests/team/unit/application/user-orga-settings.route.test.js @@ -1,5 +1,6 @@ -import * as moduleUnderTest from '../../../../lib/application/user-orga-settings/index.js'; -import { userOrgaSettingsController } from '../../../../lib/application/user-orga-settings/user-orga-settings-controller.js'; +import { securityPreHandlers } from '../../../../src/shared/application/security-pre-handlers.js'; +import { teamRoutes } from '../../../../src/team/application/routes.js'; +import { userOrgaSettingsController } from '../../../../src/team/application/user-orga-settings.controller.js'; import { expect, HttpTestServer, sinon } from '../../../test-helper.js'; describe('Unit | Router | user-orga-settings-router', function () { @@ -9,9 +10,12 @@ describe('Unit | Router | user-orga-settings-router', function () { it('should exist', async function () { // given + sinon + .stub(securityPreHandlers, 'checkRequestedUserIsAuthenticatedUser') + .callsFake((request, h) => h.response(true)); sinon.stub(userOrgaSettingsController, 'createOrUpdate').returns('ok'); const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); + await httpTestServer.register(teamRoutes); const method = 'PUT'; const url = `/api/user-orga-settings/${userId}`; @@ -39,7 +43,7 @@ describe('Unit | Router | user-orga-settings-router', function () { it('returns a 403 HTTP status code', async function () { // given const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); + await httpTestServer.register(teamRoutes); const method = 'PUT'; const url = `/api/user-orga-settings/99`; @@ -68,7 +72,7 @@ describe('Unit | Router | user-orga-settings-router', function () { it('should be mandatory', async function () { // given const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); + await httpTestServer.register(teamRoutes); const method = 'PUT'; const url = `/api/user-orga-settings/${userId}`; @@ -84,7 +88,7 @@ describe('Unit | Router | user-orga-settings-router', function () { it('should contain relationships.organization.data.id', async function () { // given const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); + await httpTestServer.register(teamRoutes); const method = 'PUT'; const url = `/api/user-orga-settings/${userId}`; @@ -111,7 +115,7 @@ describe('Unit | Router | user-orga-settings-router', function () { it('should contain relationships.organization.data.id as number', async function () { // given const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); + await httpTestServer.register(teamRoutes); const method = 'PUT'; const url = `/api/user-orga-settings/${userId}`; diff --git a/api/tests/unit/domain/usecases/create-or-update-user-orga-settings_test.js b/api/tests/team/unit/domain/usecases/create-or-update-user-orga-settings.usecase.test.js similarity index 83% rename from api/tests/unit/domain/usecases/create-or-update-user-orga-settings_test.js rename to api/tests/team/unit/domain/usecases/create-or-update-user-orga-settings.usecase.test.js index 93026475267..24ce35c3dfc 100644 --- a/api/tests/unit/domain/usecases/create-or-update-user-orga-settings_test.js +++ b/api/tests/team/unit/domain/usecases/create-or-update-user-orga-settings.usecase.test.js @@ -1,6 +1,6 @@ -import { createOrUpdateUserOrgaSettings } from '../../../../lib/domain/usecases/create-or-update-user-orga-settings.js'; -import { UserNotMemberOfOrganizationError } from '../../../../src/shared/domain/errors.js'; -import { catchErr, expect, sinon } from '../../../test-helper.js'; +import { UserNotMemberOfOrganizationError } from '../../../../../src/team/domain/errors.js'; +import { createOrUpdateUserOrgaSettings } from '../../../../../src/team/domain/usecases/create-or-update-user-orga-settings.usecase.js'; +import { catchErr, expect, sinon } from '../../../../test-helper.js'; describe('Unit | UseCase | create-or-update-user-orga-settings', function () { const userId = 1; diff --git a/api/tests/team/unit/domain/usecases/get-prescriber_test.js b/api/tests/team/unit/domain/usecases/get-prescriber_test.js index 19b9bc92bca..e1c6138ddc8 100644 --- a/api/tests/team/unit/domain/usecases/get-prescriber_test.js +++ b/api/tests/team/unit/domain/usecases/get-prescriber_test.js @@ -1,4 +1,4 @@ -import { UserNotMemberOfOrganizationError } from '../../../../../src/shared/domain/errors.js'; +import { UserNotMemberOfOrganizationError } from '../../../../../src/team/domain/errors.js'; import { getPrescriber } from '../../../../../src/team/domain/usecases/get-prescriber.js'; import { catchErr, domainBuilder, expect, sinon } from '../../../../test-helper.js'; diff --git a/api/tests/unit/infrastructure/serializers/jsonapi/user-orga-settings-serializer_test.js b/api/tests/team/unit/infrastructure/serializers/jsonapi/user-orga-settings.serializer.test.js similarity index 94% rename from api/tests/unit/infrastructure/serializers/jsonapi/user-orga-settings-serializer_test.js rename to api/tests/team/unit/infrastructure/serializers/jsonapi/user-orga-settings.serializer.test.js index eebf99cc52e..c2a45b454e5 100644 --- a/api/tests/unit/infrastructure/serializers/jsonapi/user-orga-settings-serializer_test.js +++ b/api/tests/team/unit/infrastructure/serializers/jsonapi/user-orga-settings.serializer.test.js @@ -1,6 +1,6 @@ -import * as serializer from '../../../../../lib/infrastructure/serializers/jsonapi/user-orga-settings-serializer.js'; -import { UserOrgaSettings } from '../../../../../src/shared/domain/models/UserOrgaSettings.js'; -import { domainBuilder, expect } from '../../../../test-helper.js'; +import { UserOrgaSettings } from '../../../../../../src/shared/domain/models/UserOrgaSettings.js'; +import * as serializer from '../../../../../../src/team/infrastructure/serializers/jsonapi/user-orga-settings.serializer.js'; +import { domainBuilder, expect } from '../../../../../test-helper.js'; describe('Unit | Serializer | JSONAPI | user-orga-settings-serializer', function () { describe('#serialize', function () {