From 9b336aae9f9a852b50a1a9dbe113ee37f7d135e5 Mon Sep 17 00:00:00 2001 From: Xavier Carron <33637571+xav-car@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:31:43 +0100 Subject: [PATCH 1/5] feat(api): migrate method from prescription context --- .../organization-learner-repository.js | 34 +-------- .../learner-management/domain/errors.js | 7 ++ .../organization-learner-repository.js | 31 ++++++++ api/src/shared/domain/errors.js | 7 -- .../organization-learner-repository_test.js | 73 +------------------ .../organization-learner-repository_test.js | 69 ++++++++++++++++++ .../domain/services/user-service_test.js | 4 +- 7 files changed, 111 insertions(+), 114 deletions(-) diff --git a/api/lib/infrastructure/repositories/organization-learner-repository.js b/api/lib/infrastructure/repositories/organization-learner-repository.js index 46fb49a9d5d..63fdeedf750 100644 --- a/api/lib/infrastructure/repositories/organization-learner-repository.js +++ b/api/lib/infrastructure/repositories/organization-learner-repository.js @@ -1,11 +1,6 @@ import { knex } from '../../../db/knex-database-connection.js'; import { ORGANIZATION_FEATURE } from '../../../src/shared/domain/constants.js'; -import { - NotFoundError, - OrganizationLearnerCertificabilityNotUpdatedError, - OrganizationLearnerNotFound, - UserNotFoundError, -} from '../../../src/shared/domain/errors.js'; +import { OrganizationLearnerNotFound, UserNotFoundError } from '../../../src/shared/domain/errors.js'; import { OrganizationLearner } from '../../../src/shared/domain/models/OrganizationLearner.js'; import { ParticipantRepartition } from '../../../src/shared/domain/models/ParticipantRepartition.js'; import { fetchPage } from '../../../src/shared/infrastructure/utils/knex-utils.js'; @@ -93,19 +88,6 @@ function _queryBuilderDissociation(knexConn) { }); } -const get = async function (organizationLearnerId) { - const organizationLearner = await knex - .select('*') - .from('view-active-organization-learners') - .where({ id: organizationLearnerId }) - .first(); - - if (!organizationLearner) { - throw new NotFoundError(`Student not found for ID ${organizationLearnerId}`); - } - return new OrganizationLearner(organizationLearner); -}; - const getLatestOrganizationLearner = async function ({ nationalStudentId, birthdate }) { const organizationLearner = await knex .where({ nationalStudentId, birthdate }) @@ -148,18 +130,6 @@ const isActive = async function ({ userId, campaignId }) { return !learner?.isDisabled; }; -async function updateCertificability(organizationLearner) { - const result = await knex('organization-learners').where({ id: organizationLearner.id }).update({ - isCertifiable: organizationLearner.isCertifiable, - certifiableAt: organizationLearner.certifiableAt, - }); - if (result === 0) { - throw new OrganizationLearnerCertificabilityNotUpdatedError( - `Could not update certificability for OrganizationLearner with ID ${organizationLearner.id}.`, - ); - } -} - async function countByOrganizationsWhichNeedToComputeCertificability({ skipLoggedLastDayCheck = false, onlyNotComputed = false, @@ -298,9 +268,7 @@ export { findByOrganizationIdAndUpdatedAtOrderByDivision, findByOrganizationsWhichNeedToComputeCertificability, findByUserId, - get, getLatestOrganizationLearner, isActive, - updateCertificability, updateUserIdWhereNull, }; diff --git a/api/src/prescription/learner-management/domain/errors.js b/api/src/prescription/learner-management/domain/errors.js index 10d4bb40bf2..66ffa69c93a 100644 --- a/api/src/prescription/learner-management/domain/errors.js +++ b/api/src/prescription/learner-management/domain/errors.js @@ -47,10 +47,17 @@ class CouldNotDeleteLearnersError extends DomainError { } } +class OrganizationLearnerCertificabilityNotUpdatedError extends DomainError { + constructor(message) { + super(message); + } +} + export { AggregateImportError, CouldNotDeleteLearnersError, OrganizationDoesNotHaveFeatureEnabledError, + OrganizationLearnerCertificabilityNotUpdatedError, OrganizationLearnerImportFormatNotFoundError, OrganizationLearnersCouldNotBeSavedError, ReconcileCommonOrganizationLearnerError, diff --git a/api/src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js b/api/src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js index ca360690b8d..73889ccd90a 100644 --- a/api/src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js +++ b/api/src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js @@ -8,6 +8,7 @@ import { UserCouldNotBeReconciledError, } from '../../../../shared/domain/errors.js'; import { OrganizationLearner } from '../../../../shared/domain/models/index.js'; +import { OrganizationLearnerCertificabilityNotUpdatedError } from '../../domain/errors.js'; import { CommonOrganizationLearner } from '../../domain/models/CommonOrganizationLearner.js'; import { OrganizationLearnerForAdmin } from '../../domain/read-models/OrganizationLearnerForAdmin.js'; import * as studentRepository from './student-repository.js'; @@ -302,6 +303,34 @@ const reconcileUserToOrganizationLearner = async function ({ userId, organizatio } }; +async function updateCertificability(organizationLearner) { + const knexConn = DomainTransaction.getConnection(); + const result = await knexConn('organization-learners').where({ id: organizationLearner.id }).update({ + isCertifiable: organizationLearner.isCertifiable, + certifiableAt: organizationLearner.certifiableAt, + }); + if (result === 0) { + throw new OrganizationLearnerCertificabilityNotUpdatedError( + `Could not update certificability for OrganizationLearner with ID ${organizationLearner.id}.`, + ); + } +} + +async function getLearnerInfo(organizationLearnerId) { + const knexConn = DomainTransaction.getConnection(); + + const organizationLearner = await knexConn + .select('*') + .from('view-active-organization-learners') + .where({ id: organizationLearnerId }) + .first(); + + if (!organizationLearner) { + throw new NotFoundError(`Student not found for ID ${organizationLearnerId}`); + } + return new OrganizationLearner(organizationLearner); +} + /** * @function * @name findOrganizationLearnerIdsBeforeImportFeatureFromOrganizationId @@ -325,10 +354,12 @@ export { findByUserId, findOrganizationLearnerIdsBeforeImportFeatureFromOrganizationId, findOrganizationLearnerIdsByOrganizationId, + getLearnerInfo, getOrganizationLearnerForAdmin, reconcileUserByNationalStudentIdAndOrganizationId, reconcileUserToOrganizationLearner, removeByIds, saveCommonOrganizationLearners, update, + updateCertificability, }; diff --git a/api/src/shared/domain/errors.js b/api/src/shared/domain/errors.js index 471eb646d01..c9cb89232b2 100644 --- a/api/src/shared/domain/errors.js +++ b/api/src/shared/domain/errors.js @@ -1056,12 +1056,6 @@ class AuditLoggerApiError extends DomainError { } } -class OrganizationLearnerCertificabilityNotUpdatedError extends DomainError { - constructor(message) { - super(message); - } -} - export { AccountRecoveryDemandExpired, AccountRecoveryUserAlreadyConfirmEmail, @@ -1162,7 +1156,6 @@ export { OrganizationLearnerAlreadyLinkedToInvalidUserError, OrganizationLearnerAlreadyLinkedToUserError, OrganizationLearnerCannotBeDissociatedError, - OrganizationLearnerCertificabilityNotUpdatedError, OrganizationLearnerDisabledError, OrganizationLearnerNotFound, OrganizationLearnersConstraintError, diff --git a/api/tests/integration/infrastructure/repositories/organization-learner-repository_test.js b/api/tests/integration/infrastructure/repositories/organization-learner-repository_test.js index 958d2f593be..6d30ef907d2 100644 --- a/api/tests/integration/infrastructure/repositories/organization-learner-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/organization-learner-repository_test.js @@ -4,12 +4,7 @@ import _ from 'lodash'; import { DomainTransaction } from '../../../../lib/infrastructure/DomainTransaction.js'; import * as organizationLearnerRepository from '../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import { ORGANIZATION_FEATURE } from '../../../../src/shared/domain/constants.js'; -import { - NotFoundError, - OrganizationLearnerCertificabilityNotUpdatedError, - OrganizationLearnerNotFound, - UserNotFoundError, -} from '../../../../src/shared/domain/errors.js'; +import { OrganizationLearnerNotFound, UserNotFoundError } from '../../../../src/shared/domain/errors.js'; import { OrganizationLearner } from '../../../../src/shared/domain/models/index.js'; import { catchErr, databaseBuilder, domainBuilder, expect, knex, sinon } from '../../../test-helper.js'; @@ -703,35 +698,6 @@ describe('Integration ¨| Infrastructure | Repository | organization-learner-rep }); }); - describe('#get', function () { - let organizationLearnerId; - - beforeEach(function () { - organizationLearnerId = databaseBuilder.factory.buildOrganizationLearner().id; - return databaseBuilder.commit(); - }); - - it('should return an instance of OrganizationLearner', async function () { - // when - const organizationLearner = await organizationLearnerRepository.get(organizationLearnerId); - - // then - expect(organizationLearner).to.be.an.instanceOf(OrganizationLearner); - expect(organizationLearner.id).to.equal(organizationLearnerId); - }); - - it('should return a NotFoundError if no organizationLearner is found', async function () { - // given - const nonExistentStudentId = 678; - - // when - const result = await catchErr(organizationLearnerRepository.get)(nonExistentStudentId); - - // then - expect(result).to.be.instanceOf(NotFoundError); - }); - }); - describe('#getLatestOrganizationLearner', function () { it('should return the latest organization learner', async function () { // given @@ -943,43 +909,6 @@ describe('Integration ¨| Infrastructure | Repository | organization-learner-rep }); }); - describe('#updateCertificability', function () { - it('should update isCertifiable and certifiableAt', async function () { - // given - const organizationLearner = new OrganizationLearner( - databaseBuilder.factory.buildOrganizationLearner({ isCertifiable: null, certifiableAt: null }), - ); - await databaseBuilder.commit(); - - // when - organizationLearner.isCertifiable = true; - organizationLearner.certifiableAt = new Date('2023-01-01'); - await organizationLearnerRepository.updateCertificability(organizationLearner); - - // then - const { isCertifiable, certifiableAt } = await knex('organization-learners') - .where({ id: organizationLearner.id }) - .first(); - expect(isCertifiable).to.be.true; - expect(new Date(certifiableAt)).to.deep.equal(organizationLearner.certifiableAt); - }); - - it('should throw an error if it does not update anything', async function () { - // given - const notExistingOrganizationLearner = new OrganizationLearner({ id: 1 }); - await databaseBuilder.commit(); - - // when - notExistingOrganizationLearner.isCertifiable = true; - notExistingOrganizationLearner.certifiableAt = new Date('2023-01-01'); - - const error = await catchErr(organizationLearnerRepository.updateCertificability)(notExistingOrganizationLearner); - - // then - expect(error).to.be.instanceof(OrganizationLearnerCertificabilityNotUpdatedError); - }); - }); - describe('#countByOrganizationsWhichNeedToComputeCertificability', function () { let featureId; let fromUserActivityDate; diff --git a/api/tests/prescription/learner-management/integration/infrastructure/repositories/organization-learner-repository_test.js b/api/tests/prescription/learner-management/integration/infrastructure/repositories/organization-learner-repository_test.js index 1309ac5e5ff..36f169f4e6d 100644 --- a/api/tests/prescription/learner-management/integration/infrastructure/repositories/organization-learner-repository_test.js +++ b/api/tests/prescription/learner-management/integration/infrastructure/repositories/organization-learner-repository_test.js @@ -1,6 +1,7 @@ import _ from 'lodash'; import * as organizationLearnerRepository from '../../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; +import { OrganizationLearnerCertificabilityNotUpdatedError } from '../../../../../../src/prescription/learner-management/domain/errors.js'; import { CommonOrganizationLearner } from '../../../../../../src/prescription/learner-management/domain/models/CommonOrganizationLearner.js'; import { OrganizationLearnerForAdmin } from '../../../../../../src/prescription/learner-management/domain/read-models/OrganizationLearnerForAdmin.js'; import { @@ -12,12 +13,14 @@ import { findAllCommonOrganizationLearnerByReconciliationInfos, findOrganizationLearnerIdsBeforeImportFeatureFromOrganizationId, findOrganizationLearnerIdsByOrganizationId, + getLearnerInfo, getOrganizationLearnerForAdmin, reconcileUserByNationalStudentIdAndOrganizationId, reconcileUserToOrganizationLearner, removeByIds, saveCommonOrganizationLearners, update, + updateCertificability, } from '../../../../../../src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js'; import { DomainTransaction } from '../../../../../../src/shared/domain/DomainTransaction.js'; import { @@ -2039,4 +2042,70 @@ describe('Integration | Repository | Organization Learner Management | Organizat expect([organizationLearnerIdOfYesYes]).to.be.deep.members(results); }); }); + + describe('#getLearnerInfo', function () { + let organizationLearnerId; + + beforeEach(function () { + organizationLearnerId = databaseBuilder.factory.buildOrganizationLearner().id; + return databaseBuilder.commit(); + }); + + it('should return an instance of OrganizationLearner', async function () { + // when + const organizationLearner = await getLearnerInfo(organizationLearnerId); + + // then + expect(organizationLearner).to.be.an.instanceOf(OrganizationLearner); + expect(organizationLearner.id).to.equal(organizationLearnerId); + }); + + it('should return a NotFoundError if no organizationLearner is found', async function () { + // given + const nonExistentStudentId = 678; + + // when + const result = await catchErr(getLearnerInfo)(nonExistentStudentId); + + // then + expect(result).to.be.instanceOf(NotFoundError); + }); + }); + + describe('#updateCertificability', function () { + it('should update isCertifiable and certifiableAt', async function () { + // given + const organizationLearner = new OrganizationLearner( + databaseBuilder.factory.buildOrganizationLearner({ isCertifiable: null, certifiableAt: null }), + ); + await databaseBuilder.commit(); + + // when + organizationLearner.isCertifiable = true; + organizationLearner.certifiableAt = new Date('2023-01-01'); + await updateCertificability(organizationLearner); + + // then + const { isCertifiable, certifiableAt } = await knex('organization-learners') + .where({ id: organizationLearner.id }) + .first(); + expect(isCertifiable).to.be.true; + expect(new Date(certifiableAt)).to.deep.equal(organizationLearner.certifiableAt); + }); + + it('should throw an error if it does not update anything', async function () { + // given + const notExistingOrganizationLearner = new OrganizationLearner({ id: 1 }); + await databaseBuilder.commit(); + + // when + notExistingOrganizationLearner.isCertifiable = true; + notExistingOrganizationLearner.certifiableAt = new Date('2023-01-01'); + + const error = await catchErr(updateCertificability)(notExistingOrganizationLearner); + + // then + expect(error).to.be.instanceof(OrganizationLearnerCertificabilityNotUpdatedError); + }); + }); }); diff --git a/api/tests/shared/integration/domain/services/user-service_test.js b/api/tests/shared/integration/domain/services/user-service_test.js index c9ae0a955d9..25f63d5e03e 100644 --- a/api/tests/shared/integration/domain/services/user-service_test.js +++ b/api/tests/shared/integration/domain/services/user-service_test.js @@ -1,11 +1,11 @@ import lodash from 'lodash'; const { pick } = lodash; -import * as organizationLearnerRepository from '../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../../src/identity-access-management/domain/constants/identity-providers.js'; import * as authenticationMethodRepository from '../../../../../src/identity-access-management/infrastructure/repositories/authentication-method.repository.js'; import * as userRepository from '../../../../../src/identity-access-management/infrastructure/repositories/user.repository.js'; import { userToCreateRepository } from '../../../../../src/identity-access-management/infrastructure/repositories/user-to-create.repository.js'; +import * as organizationLearnerRepository from '../../../../../src/prescription/organization-learner/infrastructure/repositories/organization-learner-repository.js'; import { OrganizationLearnerNotFound } from '../../../../../src/shared/domain/errors.js'; import * as userService from '../../../../../src/shared/domain/services/user-service.js'; import { catchErr, databaseBuilder, domainBuilder, expect, knex } from '../../../../test-helper.js'; @@ -153,7 +153,7 @@ describe('Integration | Domain | Services | user-service', function () { }); // then - const foundOrganizationLearner = await organizationLearnerRepository.get(organizationLearnerId); + const foundOrganizationLearner = await organizationLearnerRepository.getLearnerInfo(organizationLearnerId); expect(updatedUserId).to.equal(foundOrganizationLearner.userId); }); From 7259ac15c8595baa5ac10d6f484762dfa4038e33 Mon Sep 17 00:00:00 2001 From: Xavier Carron <33637571+xav-car@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:36:20 +0100 Subject: [PATCH 2/5] feat(api): use prescription repo on needed usecase --- ...nerate-username-with-temporary-password.js | 4 +-- ...ization-learner-dependent-user-password.js | 4 +-- .../domain/services/user-service_test.js | 5 ++-- ...e-username-with-temporary-password_test.js | 22 +++++++++------- ...on-learner-dependent-user-password_test.js | 26 ++++++++++--------- 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/api/lib/domain/usecases/generate-username-with-temporary-password.js b/api/lib/domain/usecases/generate-username-with-temporary-password.js index defd0e829a7..d96823fedd9 100644 --- a/api/lib/domain/usecases/generate-username-with-temporary-password.js +++ b/api/lib/domain/usecases/generate-username-with-temporary-password.js @@ -13,9 +13,9 @@ const generateUsernameWithTemporaryPassword = async function ({ userService, authenticationMethodRepository, userRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, }) { - const organizationLearner = await organizationLearnerRepository.get(organizationLearnerId); + const organizationLearner = await prescriptionOrganizationLearnerRepository.getLearnerInfo(organizationLearnerId); _checkIfStudentHasAccessToOrganization(organizationLearner, organizationId); const studentAccount = await userRepository.get(organizationLearner.userId); diff --git a/api/lib/domain/usecases/update-organization-learner-dependent-user-password.js b/api/lib/domain/usecases/update-organization-learner-dependent-user-password.js index b29f2985d82..a170cf4b270 100644 --- a/api/lib/domain/usecases/update-organization-learner-dependent-user-password.js +++ b/api/lib/domain/usecases/update-organization-learner-dependent-user-password.js @@ -11,11 +11,11 @@ const updateOrganizationLearnerDependentUserPassword = async function ({ cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }) { const userWithMemberships = await userRepository.getWithMemberships(userId); - const organizationLearner = await organizationLearnerRepository.get(organizationLearnerId); + const organizationLearner = await prescriptionOrganizationLearnerRepository.getLearnerInfo(organizationLearnerId); if ( !userWithMemberships.hasAccessToOrganization(organizationId) || diff --git a/api/tests/shared/integration/domain/services/user-service_test.js b/api/tests/shared/integration/domain/services/user-service_test.js index 25f63d5e03e..c0e6a932574 100644 --- a/api/tests/shared/integration/domain/services/user-service_test.js +++ b/api/tests/shared/integration/domain/services/user-service_test.js @@ -1,11 +1,12 @@ import lodash from 'lodash'; const { pick } = lodash; +import * as organizationLearnerRepository from '../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../../src/identity-access-management/domain/constants/identity-providers.js'; import * as authenticationMethodRepository from '../../../../../src/identity-access-management/infrastructure/repositories/authentication-method.repository.js'; import * as userRepository from '../../../../../src/identity-access-management/infrastructure/repositories/user.repository.js'; import { userToCreateRepository } from '../../../../../src/identity-access-management/infrastructure/repositories/user-to-create.repository.js'; -import * as organizationLearnerRepository from '../../../../../src/prescription/organization-learner/infrastructure/repositories/organization-learner-repository.js'; +import { getLearnerInfo } from '../../../../../src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js'; import { OrganizationLearnerNotFound } from '../../../../../src/shared/domain/errors.js'; import * as userService from '../../../../../src/shared/domain/services/user-service.js'; import { catchErr, databaseBuilder, domainBuilder, expect, knex } from '../../../../test-helper.js'; @@ -153,7 +154,7 @@ describe('Integration | Domain | Services | user-service', function () { }); // then - const foundOrganizationLearner = await organizationLearnerRepository.getLearnerInfo(organizationLearnerId); + const foundOrganizationLearner = await getLearnerInfo(organizationLearnerId); expect(updatedUserId).to.equal(foundOrganizationLearner.userId); }); diff --git a/api/tests/unit/domain/usecases/generate-username-with-temporary-password_test.js b/api/tests/unit/domain/usecases/generate-username-with-temporary-password_test.js index a647ef67eaf..af6eefa9f05 100644 --- a/api/tests/unit/domain/usecases/generate-username-with-temporary-password_test.js +++ b/api/tests/unit/domain/usecases/generate-username-with-temporary-password_test.js @@ -20,7 +20,7 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function let authenticationMethodRepository; let userRepository; - let organizationLearnerRepository; + let prescriptionOrganizationLearnerRepository; beforeEach(function () { userRelatedToStudent = domainBuilder.buildUser({ @@ -55,10 +55,12 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function get: sinon.stub().resolves(userRelatedToStudent), updateUsernameAndPassword: sinon.stub().resolves(), }; - organizationLearnerRepository = { - get: sinon.stub(), + prescriptionOrganizationLearnerRepository = { + getLearnerInfo: sinon.stub(), }; - organizationLearnerRepository.get.withArgs(organizationLearnerId).resolves(organizationLearner); + prescriptionOrganizationLearnerRepository.getLearnerInfo + .withArgs(organizationLearnerId) + .resolves(organizationLearner); }); it('should generate username and temporary password', async function () { @@ -72,7 +74,7 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function userService, authenticationMethodRepository, userRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, }); // then @@ -94,7 +96,7 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function userService, authenticationMethodRepository, userRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, }); // then @@ -119,7 +121,7 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function userService, authenticationMethodRepository, userRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, }); // then @@ -152,7 +154,9 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function userReconciliationService.createUsernameByUser.resolves(username); - organizationLearnerRepository.get.withArgs(organizationLearner.id).resolves(organizationLearner); + prescriptionOrganizationLearnerRepository.getLearnerInfo + .withArgs(organizationLearner.id) + .resolves(organizationLearner); userRepository.get.resolves(userWithEmail); userRepository.updateUsername.resolves({ ...userWithEmail, username }); @@ -170,7 +174,7 @@ describe('Unit | UseCase | generate-username-with-temporary-password', function userService, authenticationMethodRepository, userRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, }); // then diff --git a/api/tests/unit/domain/usecases/update-organization-learner-dependent-user-password_test.js b/api/tests/unit/domain/usecases/update-organization-learner-dependent-user-password_test.js index 948339ea45f..3549fff0eaf 100644 --- a/api/tests/unit/domain/usecases/update-organization-learner-dependent-user-password_test.js +++ b/api/tests/unit/domain/usecases/update-organization-learner-dependent-user-password_test.js @@ -14,7 +14,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', let passwordGenerator; let cryptoService; let authenticationMethodRepository; - let organizationLearnerRepository; + let prescriptionOrganizationLearnerRepository; let userRepository; let userMember; @@ -47,8 +47,8 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', authenticationMethodRepository = { updatePasswordThatShouldBeChanged: sinon.stub(), }; - organizationLearnerRepository = { - get: sinon.stub().resolves(student), + prescriptionOrganizationLearnerRepository = { + getLearnerInfo: sinon.stub().resolves(student), }; userRepository = { get: sinon.stub().resolves(userStudent), @@ -66,7 +66,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -83,12 +83,14 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); // then - expect(organizationLearnerRepository.get).to.have.been.calledWithExactly(organizationLearnerId); + expect(prescriptionOrganizationLearnerRepository.getLearnerInfo).to.have.been.calledWithExactly( + organizationLearnerId, + ); }); it('should update user password with a hashed password', async function () { @@ -100,7 +102,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -121,7 +123,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -142,7 +144,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -167,7 +169,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -189,7 +191,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); @@ -210,7 +212,7 @@ describe('Unit | UseCase | update-organization-learner-dependent-user-password', cryptoService, passwordGenerator, authenticationMethodRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, }); From d136bd47316b918b8f6cca60d4813b4faea67a1a Mon Sep 17 00:00:00 2001 From: Xavier Carron <33637571+xav-car@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:36:47 +0100 Subject: [PATCH 3/5] feat(api): update usecase identity access management --- .../usecases/get-account-recovery-details.usecase.js | 6 +++--- .../identity-access-management/domain/usecases/index.js | 2 ++ .../usecases/get-account-recovery-details.usecase.test.js | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/api/src/identity-access-management/domain/usecases/get-account-recovery-details.usecase.js b/api/src/identity-access-management/domain/usecases/get-account-recovery-details.usecase.js index 80afa708d6c..b27cc2158bb 100644 --- a/api/src/identity-access-management/domain/usecases/get-account-recovery-details.usecase.js +++ b/api/src/identity-access-management/domain/usecases/get-account-recovery-details.usecase.js @@ -2,7 +2,7 @@ * @param {{ * temporaryKey: string, * accountRecoveryDemandRepository: AccountRecoveryDemandRepository, - * organizationLearnerRepository: OrganizationLearnerRepository, + * prescriptionOrganizationLearnerRepository: PrescriptionOrganizationLearnerRepository, * userRepository: UserRepository, * scoAccountRecoveryService: ScoAccountRecoveryService, * }} params @@ -11,7 +11,7 @@ export const getAccountRecoveryDetails = async function ({ temporaryKey, accountRecoveryDemandRepository, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, userRepository, scoAccountRecoveryService, }) { @@ -22,7 +22,7 @@ export const getAccountRecoveryDetails = async function ({ userRepository, }); - const { firstName } = await organizationLearnerRepository.get(organizationLearnerId); + const { firstName } = await prescriptionOrganizationLearnerRepository.getLearnerInfo(organizationLearnerId); return { id, diff --git a/api/src/identity-access-management/domain/usecases/index.js b/api/src/identity-access-management/domain/usecases/index.js index 480e5bc9670..a05cc1c7ee4 100644 --- a/api/src/identity-access-management/domain/usecases/index.js +++ b/api/src/identity-access-management/domain/usecases/index.js @@ -10,6 +10,7 @@ import * as campaignRepository from '../../../../lib/infrastructure/repositories import * as organizationLearnerRepository from '../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import * as userRecommendedTrainingRepository from '../../../devcomp/infrastructure/repositories/user-recommended-training-repository.js'; import { repositories as campaignRepositories } from '../../../prescription/campaign/infrastructure/repositories/index.js'; +import * as prescriptionOrganizationLearnerRepository from '../../../prescription/learner-management/infrastructure/repositories/organization-learner-repository.js'; import { config } from '../../../shared/config.js'; import { cryptoService } from '../../../shared/domain/services/crypto-service.js'; import { tokenService } from '../../../shared/domain/services/token-service.js'; @@ -59,6 +60,7 @@ const repositories = { membershipRepository, oidcProviderRepository, organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, privacyUsersApiRepository, refreshTokenRepository, resetPasswordDemandRepository, diff --git a/api/tests/identity-access-management/unit/domain/usecases/get-account-recovery-details.usecase.test.js b/api/tests/identity-access-management/unit/domain/usecases/get-account-recovery-details.usecase.test.js index 185a38f4c9c..e9c66edf233 100644 --- a/api/tests/identity-access-management/unit/domain/usecases/get-account-recovery-details.usecase.test.js +++ b/api/tests/identity-access-management/unit/domain/usecases/get-account-recovery-details.usecase.test.js @@ -5,8 +5,8 @@ describe('Unit | Identity Access Management | Domain | UseCase | get-account-rec it('returns new email and firstName of account recovery demand', async function () { // given const temporaryKey = 'ZHABCDEFJSJ'; - const organizationLearnerRepository = { - get: sinon.stub(), + const prescriptionOrganizationLearnerRepository = { + getLearnerInfo: sinon.stub(), }; const scoAccountRecoveryService = { retrieveAndValidateAccountRecoveryDemand: sinon.stub(), @@ -16,12 +16,12 @@ describe('Unit | Identity Access Management | Domain | UseCase | get-account-rec const firstName = 'Emma'; scoAccountRecoveryService.retrieveAndValidateAccountRecoveryDemand.resolves({ organizationLearnerId, newEmail }); - organizationLearnerRepository.get.withArgs(organizationLearnerId).resolves({ firstName }); + prescriptionOrganizationLearnerRepository.getLearnerInfo.withArgs(organizationLearnerId).resolves({ firstName }); // when const result = await getAccountRecoveryDetails({ temporaryKey, - organizationLearnerRepository, + prescriptionOrganizationLearnerRepository, scoAccountRecoveryService, }); From 86dd9e127b50600c3f7eb98e9d409e1d19599f50 Mon Sep 17 00:00:00 2001 From: Xavier Carron <33637571+xav-car@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:37:54 +0100 Subject: [PATCH 4/5] feat(api): use method from prescription context --- .../usecases/checkUserBelongsToLearnersOrganization.js | 4 ++-- .../usecases/checkUserBelongsToLearnersOrganization_test.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/shared/application/usecases/checkUserBelongsToLearnersOrganization.js b/api/src/shared/application/usecases/checkUserBelongsToLearnersOrganization.js index f70bd90414f..bfabb37caaa 100644 --- a/api/src/shared/application/usecases/checkUserBelongsToLearnersOrganization.js +++ b/api/src/shared/application/usecases/checkUserBelongsToLearnersOrganization.js @@ -1,6 +1,6 @@ import _ from 'lodash'; -import * as organizationLearnerRepository from '../../../../lib/infrastructure/repositories/organization-learner-repository.js'; +import * as organizationLearnerRepository from '../../../../src/prescription/learner-management/infrastructure/repositories/organization-learner-repository.js'; import * as membershipRepository from '../../../team/infrastructure/repositories/membership.repository.js'; const execute = async function ( @@ -8,7 +8,7 @@ const execute = async function ( organizationLearnerId, dependencies = { membershipRepository, organizationLearnerRepository }, ) { - const organizationLearner = await dependencies.organizationLearnerRepository.get(organizationLearnerId); + const organizationLearner = await dependencies.organizationLearnerRepository.getLearnerInfo(organizationLearnerId); const memberships = await dependencies.membershipRepository.findByUserIdAndOrganizationId({ userId, organizationId: organizationLearner.organizationId, diff --git a/api/tests/shared/unit/application/usecases/checkUserBelongsToLearnersOrganization_test.js b/api/tests/shared/unit/application/usecases/checkUserBelongsToLearnersOrganization_test.js index f9d80cb1afe..836c4c3bf5b 100644 --- a/api/tests/shared/unit/application/usecases/checkUserBelongsToLearnersOrganization_test.js +++ b/api/tests/shared/unit/application/usecases/checkUserBelongsToLearnersOrganization_test.js @@ -10,7 +10,7 @@ describe('Unit | Application | Use Case | checkUserBelongsToLearnersOrganization findByUserIdAndOrganizationId: sinon.stub(), }; organizationLearnerRepositoryStub = { - get: sinon.stub(), + getLearnerInfo: sinon.stub(), }; }); @@ -26,7 +26,7 @@ describe('Unit | Application | Use Case | checkUserBelongsToLearnersOrganization id: organizationLearnerId, organization: sharedOrganization, }); - organizationLearnerRepositoryStub.get.resolves(organizationLearner); + organizationLearnerRepositoryStub.getLearnerInfo.resolves(organizationLearner); membershipRepositoryStub.findByUserIdAndOrganizationId.resolves([membership]); // when @@ -54,7 +54,7 @@ describe('Unit | Application | Use Case | checkUserBelongsToLearnersOrganization id: organizationLearnerId, anotherOrganization, }); - organizationLearnerRepositoryStub.get.resolves(organizationLearner); + organizationLearnerRepositoryStub.getLearnerInfo.resolves(organizationLearner); membershipRepositoryStub.findByUserIdAndOrganizationId.resolves([]); // when From 3a82dd9cae7bf4c3a91d9a394dfd3d34b36ffd1a Mon Sep 17 00:00:00 2001 From: Xavier Carron <33637571+xav-car@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:38:15 +0100 Subject: [PATCH 5/5] feat(api): migrate usecase and use into controller --- .../jobs/compute-certificability-job-controller.js | 2 +- .../compute-organization-learner-certificability.js | 2 +- .../learner-management/domain/usecases/index.js | 4 ++++ .../jobs/compute-certificability-job-controller_test.js | 2 +- .../compute-organization-learner-certificability_test.js | 8 ++++---- 5 files changed, 11 insertions(+), 7 deletions(-) rename api/{lib => src/prescription/learner-management}/domain/usecases/compute-organization-learner-certificability.js (93%) rename api/tests/{ => prescription/organization-learner}/unit/domain/usecases/compute-organization-learner-certificability_test.js (81%) diff --git a/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js b/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js index 005f4ec759f..ac1ad4af510 100644 --- a/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js +++ b/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js @@ -1,4 +1,4 @@ -import { usecases } from '../../../../../lib/domain/usecases/index.js'; +import { usecases } from '../../../../../src/prescription/learner-management/domain/usecases/index.js'; import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { ComputeCertificabilityJob } from '../../domain/models/ComputeCertificabilityJob.js'; diff --git a/api/lib/domain/usecases/compute-organization-learner-certificability.js b/api/src/prescription/learner-management/domain/usecases/compute-organization-learner-certificability.js similarity index 93% rename from api/lib/domain/usecases/compute-organization-learner-certificability.js rename to api/src/prescription/learner-management/domain/usecases/compute-organization-learner-certificability.js index 798e14c42c7..1bb335310d1 100644 --- a/api/lib/domain/usecases/compute-organization-learner-certificability.js +++ b/api/src/prescription/learner-management/domain/usecases/compute-organization-learner-certificability.js @@ -3,7 +3,7 @@ const computeOrganizationLearnerCertificability = async function ({ organizationLearnerRepository, placementProfileService, }) { - const organizationLearner = await organizationLearnerRepository.get(organizationLearnerId); + const organizationLearner = await organizationLearnerRepository.getLearnerInfo(organizationLearnerId); const placementProfile = await placementProfileService.getPlacementProfile({ userId: organizationLearner.userId, diff --git a/api/src/prescription/learner-management/domain/usecases/index.js b/api/src/prescription/learner-management/domain/usecases/index.js index 3e54cfdf757..98c1e457b32 100644 --- a/api/src/prescription/learner-management/domain/usecases/index.js +++ b/api/src/prescription/learner-management/domain/usecases/index.js @@ -7,6 +7,7 @@ import * as campaignRepository from '../../../../../lib/infrastructure/repositor import * as libOrganizationLearnerRepository from '../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import * as userRepository from '../../../../identity-access-management/infrastructure/repositories/user.repository.js'; import * as organizationFeatureApi from '../../../../organizational-entities/application/api/organization-features-api.js'; +import * as placementProfileService from '../../../../shared/domain/services/placement-profile-service.js'; import { logErrorWithCorrelationIds } from '../../../../shared/infrastructure/monitoring-tools.js'; import * as organizationRepository from '../../../../shared/infrastructure/repositories/organization-repository.js'; import { injectDependencies } from '../../../../shared/infrastructure/utils/dependency-injection.js'; @@ -46,6 +47,7 @@ import { importStorage } from '../../infrastructure/storage/import-storage.js'; * @typedef {import ('../../infrastructure/repositories/organization-learner-import-format-repository.js')} OrganizationLearnerImportFormatRepository * @typedef {import ('../../infrastructure/repositories/organization-learner-repository.js')} OrganizationLearnerRepository * @typedef {import ('../../../../shared/infrastructure/repositories/organization-repository.js')} OrganizationRepository + * @typedef {import ('../../../../shared/domain/services/placement-profile-service.js')} placementProfileService * @typedef {import('../../../organization-learner/infrastructure/repositories/registration-organization-learner-repository.js')} registrationOrganizationLearnerRepository * @typedef {import ('../../infrastructure/repositories/student-repository.js')} studentRepository * @typedef {import ('../../infrastructure/repositories/sup-organization-learner-repository.js')} SupOrganizationLearnerRepository @@ -72,6 +74,7 @@ const dependencies = { organizationLearnerImportFormatRepository, organizationLearnerRepository, organizationRepository, + placementProfileService, registrationOrganizationLearnerRepository, studentRepository, supOrganizationLearnerRepository, @@ -96,6 +99,7 @@ const usecasesWithoutInjectedDependencies = { /** * @typedef PrescriptionLearnerManagementUsecases + * @property {computeOrganizationLearnerCertificability} computeOrganizationLearnerCertificability * @property {saveOrganizationLearnersFile} saveOrganizationLearnersFile * @property {sendOrganizationLearnersFile} sendOrganizationLearnersFile * @property {validateOrganizationLearnersFile} validateOrganizationLearnersFile diff --git a/api/tests/prescription/learner-management/unit/application/jobs/compute-certificability-job-controller_test.js b/api/tests/prescription/learner-management/unit/application/jobs/compute-certificability-job-controller_test.js index eeeb64d64cc..b5b1cb27f09 100644 --- a/api/tests/prescription/learner-management/unit/application/jobs/compute-certificability-job-controller_test.js +++ b/api/tests/prescription/learner-management/unit/application/jobs/compute-certificability-job-controller_test.js @@ -1,5 +1,5 @@ -import { usecases } from '../../../../../../lib/domain/usecases/index.js'; import { ComputeCertificabilityJobController } from '../../../../../../src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js'; +import { usecases } from '../../../../../../src/prescription/learner-management/domain/usecases/index.js'; import { expect, sinon } from '../../../../../test-helper.js'; describe('Unit | Prescription | Application | Jobs | computeCertificabilityJobController', function () { diff --git a/api/tests/unit/domain/usecases/compute-organization-learner-certificability_test.js b/api/tests/prescription/organization-learner/unit/domain/usecases/compute-organization-learner-certificability_test.js similarity index 81% rename from api/tests/unit/domain/usecases/compute-organization-learner-certificability_test.js rename to api/tests/prescription/organization-learner/unit/domain/usecases/compute-organization-learner-certificability_test.js index e4af604a368..006db9f9cfe 100644 --- a/api/tests/unit/domain/usecases/compute-organization-learner-certificability_test.js +++ b/api/tests/prescription/organization-learner/unit/domain/usecases/compute-organization-learner-certificability_test.js @@ -1,5 +1,5 @@ -import { usecases } from '../../../../lib/domain/usecases/index.js'; -import { domainBuilder, expect, sinon } from '../../../test-helper.js'; +import { usecases } from '../../../../../../src/prescription/learner-management/domain/usecases/index.js'; +import { domainBuilder, expect, sinon } from '../../../../../test-helper.js'; describe('Unit | UseCase | compute-organization-learner-certificabilty', function () { let clock; @@ -18,13 +18,13 @@ describe('Unit | UseCase | compute-organization-learner-certificabilty', functio it('should update certificability for an organization learner', async function () { // given const organizationLearnerRepository = { - get: sinon.stub(), + getLearnerInfo: sinon.stub(), updateCertificability: sinon.stub(), }; const organizationLearnerId = 1; const organizationLearner = domainBuilder.buildOrganizationLearner({ id: organizationLearnerId }); sinon.stub(organizationLearner, 'updateCertificability'); - organizationLearnerRepository.get.withArgs(organizationLearner.id).returns(organizationLearner); + organizationLearnerRepository.getLearnerInfo.withArgs(organizationLearner.id).returns(organizationLearner); const placementProfileService = { getPlacementProfile: sinon.stub(), };