Skip to content

Commit

Permalink
[FEATURE] Ajouter la fonctionnalité d'import ONDE aux organisations a…
Browse files Browse the repository at this point in the history
…joutées depuis le script de création en masse (PIX-12563).

 #9449
  • Loading branch information
pix-service-auto-merge committed Jul 8, 2024
2 parents 09280bc + 07c619d commit 4b9972e
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 379 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const createOrganizationsWithTagsAndTargetProfiles = async function ({
dataProtectionOfficerRepository,
organizationInvitationRepository,
organizationRepository,
organizationForAdminRepository,
organizationTagRepository,
schoolRepository,
tagRepository,
Expand All @@ -46,7 +47,7 @@ const createOrganizationsWithTagsAndTargetProfiles = async function ({

createdOrganizations = await _createOrganizations({
domainTransaction,
organizationRepository,
organizationForAdminRepository,
transformedOrganizationsData,
});

Expand Down Expand Up @@ -84,27 +85,36 @@ const createOrganizationsWithTagsAndTargetProfiles = async function ({

export { createOrganizationsWithTagsAndTargetProfiles };

async function _createOrganizations({ transformedOrganizationsData, domainTransaction, organizationRepository }) {
try {
const createdOrganizations = await organizationRepository.batchCreateOrganizations(
transformedOrganizationsData,
domainTransaction,
);
return createdOrganizations;
} catch (error) {
_monitorError(error.message, { error, event: 'create-organizations' });

if (error.code === PGSQL_FOREIGN_KEY_VIOLATION_ERROR) {
const createdByUserId = error.detail.match(/\d+/g);
throw new InvalidInputDataError({ message: `User with ID "${createdByUserId}" does not exist` });
}

if (error instanceof DomainError) {
throw error;
async function _createOrganizations({
transformedOrganizationsData,
domainTransaction,
organizationForAdminRepository,
}) {
return bluebird.map(transformedOrganizationsData, async (organizationToCreate) => {
try {
const createdOrganization = await organizationForAdminRepository.save(
organizationToCreate.organization,
domainTransaction,
);
return {
createdOrganization,
organizationToCreate,
};
} catch (error) {
_monitorError(error.message, { error, event: 'create-organizations' });

if (error.code === PGSQL_FOREIGN_KEY_VIOLATION_ERROR) {
const createdByUserId = error.detail.match(/\d+/g);
throw new InvalidInputDataError({ message: `User with ID "${createdByUserId}" does not exist` });
}

if (error instanceof DomainError) {
throw error;
}

throw new DomainError(error.message);
}

throw new DomainError(error.message);
}
});
}

function _transformOrganizationsCsvData(organizationsCsvData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,25 @@ const get = async function (id, domainTransaction = DomainTransaction.emptyTrans
* @param {OrganizationForAdmin} organization
* @return {Promise<OrganizationForAdmin>}
*/
const save = async function (organization) {
const data = _.pick(organization, ['name', 'type', 'documentationUrl', 'credit', 'createdBy']);
const [organizationCreated] = await knex(ORGANIZATIONS_TABLE_NAME).returning('*').insert(data);
const save = async function (organization, domainTransaction = DomainTransaction.emptyTransaction()) {
const knexConn = domainTransaction.knexTransaction ?? knex;
const data = _.pick(organization, [
'name',
'type',
'email',
'externalId',
'provinceCode',
'isManagingStudents',
'identityProviderForCampaigns',
'credit',
'createdBy',
'documentationUrl',
]);
const [organizationCreated] = await knexConn(ORGANIZATIONS_TABLE_NAME).returning('*').insert(data);
const savedOrganization = _toDomain(organizationCreated);

if (!_.isEmpty(savedOrganization.features)) {
await _enableFeatures(knex, savedOrganization.features, savedOrganization.id);
await _enableFeatures(knexConn, savedOrganization.features, savedOrganization.id);
}
return savedOrganization;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import bluebird from 'bluebird';
import _ from 'lodash';

import { knex } from '../../../../db/knex-database-connection.js';
import { DomainTransaction } from '../../../../lib/infrastructure/DomainTransaction.js';
import { Organization } from '../../../organizational-entities/domain/models/Organization.js';
import { Tag } from '../../../organizational-entities/domain/models/Tag.js';
import { NotFoundError } from '../../domain/errors.js';
import { CONCURRENCY_HEAVY_OPERATIONS } from '../constants.js';
import { fetchPage } from '../utils/knex-utils.js';

const ORGANIZATIONS_TABLE_NAME = 'organizations';
Expand Down Expand Up @@ -75,56 +72,6 @@ const create = function (organization) {
.then(([organization]) => _toDomain(organization));
};

// Uses OrganizationForAdmin to centralize features enablement by organization type
const batchCreateOrganizations = async function (
organizations,
domainTransaction = DomainTransaction.emptyTransaction(),
) {
const knexConn = domainTransaction.knexTransaction ?? knex;
const featuresByKey = _.keyBy(await knexConn('features'), (feature) => feature.key);

return bluebird.map(
organizations,
async (organizationCsvData) => {
const { organization } = organizationCsvData;
const [createdOrganization] = await knexConn(ORGANIZATIONS_TABLE_NAME)
.insert(
_.pick(organization, [
'name',
'type',
'email',
'externalId',
'provinceCode',
'isManagingStudents',
'identityProviderForCampaigns',
'credit',
'createdBy',
'documentationUrl',
]),
)
.returning('*');

const enabledFeatures = _.keys(organization.features).filter((key) => organization.features[key] === true);

for (const featureKey of enabledFeatures) {
const feature = featuresByKey[featureKey];
await knexConn('organization-features').insert({
organizationId: createdOrganization.id,
featureId: feature.id,
});
}

return {
createdOrganization,
organizationToCreate: organizationCsvData,
};
},
{
concurrency: CONCURRENCY_HEAVY_OPERATIONS,
},
);
};

const update = async function (organization) {
const organizationRawData = _.pick(organization, [
'name',
Expand Down Expand Up @@ -243,7 +190,6 @@ const findPaginatedFilteredByTargetProfile = async function ({ targetProfileId,
};

export {
batchCreateOrganizations,
create,
findActiveScoOrganizationsByExternalId,
findByExternalIdsFetchingIdsOnly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as dataProtectionOfficerRepository from '../../../../lib/infrastructure
import * as organizationTagRepository from '../../../../lib/infrastructure/repositories/organization-tag-repository.js';
import * as tagRepository from '../../../../lib/infrastructure/repositories/tag-repository.js';
import * as targetProfileShareRepository from '../../../../lib/infrastructure/repositories/target-profile-share-repository.js';
import { organizationForAdminRepository } from '../../../../src/organizational-entities/infrastructure/repositories/organization-for-admin.repository.js';
import * as schoolRepository from '../../../../src/school/infrastructure/repositories/school-repository.js';
import { ORGANIZATION_FEATURE } from '../../../../src/shared/domain/constants.js';
import { EntityValidationError } from '../../../../src/shared/domain/errors.js';
Expand All @@ -25,11 +26,18 @@ const { omit } = lodash;

describe('Integration | UseCases | create-organizations-with-tags-and-target-profiles', function () {
let missionFeature;
let importStudentsFeature;
let ondeImportFormat;
let userId;

beforeEach(async function () {
databaseBuilder.factory.buildFeature(ORGANIZATION_FEATURE.COMPUTE_ORGANIZATION_LEARNER_CERTIFICABILITY);
missionFeature = databaseBuilder.factory.buildFeature(ORGANIZATION_FEATURE.MISSIONS_MANAGEMENT);
importStudentsFeature = databaseBuilder.factory.buildFeature(ORGANIZATION_FEATURE.LEARNER_IMPORT);
ondeImportFormat = databaseBuilder.factory.buildOrganizationLearnerImportFormat({
name: ORGANIZATION_FEATURE.LEARNER_IMPORT.FORMAT.ONDE,
});

userId = databaseBuilder.factory.buildUser().id;
await databaseBuilder.commit();
});
Expand All @@ -45,6 +53,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -84,6 +93,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithEmptyValues,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -185,6 +195,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithTagsWithOneMissingExternalId,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -258,6 +269,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithTagsWithOneMissingName,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -334,6 +346,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithTagsNotExists,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -413,6 +426,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithTagsAlreadyExist,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -511,6 +525,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithNonExistingTargetProfile,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -589,6 +604,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithExistingTargetProfiles,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -675,6 +691,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations: organizationsWithInvitationRole,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand All @@ -697,7 +714,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
});

describe('when organization type is SCO-1D', function () {
it('should add mission management feature to organization', async function () {
it('should add mission management and ONDE import features to organization', async function () {
// given
databaseBuilder.factory.buildTag({ name: 'TAG1' });
await databaseBuilder.commit();
Expand All @@ -719,10 +736,11 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
];

// when
await createOrganizationsWithTagsAndTargetProfiles({
const createdOrganizations = await createOrganizationsWithTagsAndTargetProfiles({
domainTransaction,
organizations,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand All @@ -735,12 +753,20 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro

// then
const savedOrganizationFeatures = await knex('organization-features');
//TODO organization create with batch, see https://1024pix.atlassian.net/jira/software/c/projects/PIX/boards/107?selectedIssue=PIX-12563
expect(savedOrganizationFeatures.length).to.equal(1);
const savedOrganizationFeatureIds = savedOrganizationFeatures.map(
(organizationFeature) => organizationFeature.featureId,
);
expect(savedOrganizationFeatureIds).to.include(missionFeature.id);
expect(savedOrganizationFeatures.length).to.equal(2);
const organizationId = createdOrganizations[0].id;
expect(savedOrganizationFeatures.map((organizationFeature) => omit(organizationFeature, 'id'))).to.deep.equal([
{
featureId: missionFeature.id,
params: null,
organizationId,
},
{
featureId: importStudentsFeature.id,
params: { organizationLearnerImportFormatId: ondeImportFormat.id },
organizationId,
},
]);
});

it('should create schools associated to organizations', async function () {
Expand Down Expand Up @@ -782,6 +808,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down Expand Up @@ -843,6 +870,7 @@ describe('Integration | UseCases | create-organizations-with-tags-and-target-pro
domainTransaction,
organizations,
organizationRepository,
organizationForAdminRepository,
tagRepository,
targetProfileShareRepository,
organizationTagRepository,
Expand Down
Loading

0 comments on commit 4b9972e

Please sign in to comment.