Skip to content

Commit

Permalink
[FEATURE] Ne recuperer que les attestations partagées pour le prescri…
Browse files Browse the repository at this point in the history
…pteur (PIX-15336)

 #10594
  • Loading branch information
pix-service-auto-merge authored Nov 20, 2024
2 parents bf67163 + 6e69fa0 commit da12267
Show file tree
Hide file tree
Showing 19 changed files with 480 additions and 31 deletions.
33 changes: 24 additions & 9 deletions api/db/seeds/data/team-prescription/build-quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { REWARD_TYPES } from '../../../../src/quest/domain/constants.js';
import { COMPARISON } from '../../../../src/quest/domain/models/Quest.js';
import { Assessment, CampaignParticipationStatuses } from '../../../../src/shared/domain/models/index.js';
import { temporaryStorage } from '../../../../src/shared/infrastructure/temporary-storage/index.js';
import { AEFE_TAG, FEATURE_ATTESTATIONS_MANAGEMENT_ID } from '../common/constants.js';
import { AEFE_TAG, FEATURE_ATTESTATIONS_MANAGEMENT_ID, USER_ID_ADMIN_ORGANIZATION } from '../common/constants.js';
import { TARGET_PROFILE_BADGES_STAGES_ID } from './constants.js';

const profileRewardTemporaryStorage = temporaryStorage.withPrefix('profile-rewards:');
Expand Down Expand Up @@ -81,7 +81,7 @@ const buildOrganization = (databaseBuilder) => databaseBuilder.factory.buildOrga
const buildOrganizationLearners = (databaseBuilder, organization, users) =>
users.map((user) =>
databaseBuilder.factory.buildOrganizationLearner({
userId: user.id,
...user,
organizationId: organization.id,
}),
);
Expand Down Expand Up @@ -199,6 +199,14 @@ export const buildQuests = async (databaseBuilder) => {

const organization = buildOrganization(databaseBuilder);

// Add [email protected] as Admin in organization

databaseBuilder.factory.buildMembership({
organizationId: organization.id,
organizationRole: 'ADMIN',
userId: USER_ID_ADMIN_ORGANIZATION,
});

// Associate attestation feature to organization

databaseBuilder.factory.buildOrganizationFeature({
Expand All @@ -212,18 +220,25 @@ export const buildQuests = async (databaseBuilder) => {

// Create organizationLearners

const organizationLearnersData = [
{ userId: successUser.id, division: '6emeA', firstName: 'attestation-success', lastName: 'attestation-success' },
{
userId: successSharedUser.id,
division: '6emeA',
firstName: 'attestation-success-shared',
lastName: 'attestation-success-shared',
},
{ userId: failedUser.id, division: '6emeA', firstName: 'attestation-failed', lastName: 'attestation-failed' },
{ userId: pendingUser.id, division: '6emeB', firstName: 'attestation-pending', lastName: 'attestation-pending' },
{ userId: blankUser.id, division: '6emeB', firstName: 'attestation-blank', lastName: 'attestation-blank' },
];

const [
successOrganizationLearner,
successSharedOrganizationLearner,
failedOrganizationLearner,
pendingOrganizationLearner,
] = buildOrganizationLearners(databaseBuilder, organization, [
successUser,
successSharedUser,
failedUser,
pendingUser,
blankUser,
]);
] = buildOrganizationLearners(databaseBuilder, organization, organizationLearnersData);

// Create target profile

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { NoProfileRewardsFoundError } from '../../../profile/domain/errors.js';
import { usecases } from '../domain/usecases/index.js';

const getAttestationZipForDivisions = async function (request, h) {
const organizationId = request.params.organizationId;
const attestationKey = request.params.attestationKey;
const divisions = request.query.divisions;

const buffer = await usecases.getAttestationZipForDivisions({ attestationKey, organizationId, divisions });

return h.response(buffer).header('Content-Type', 'application/zip');
try {
const buffer = await usecases.getAttestationZipForDivisions({ attestationKey, organizationId, divisions });
return h.response(buffer).header('Content-Type', 'application/zip');
} catch (error) {
if (error instanceof NoProfileRewardsFoundError) {
return h.response().code(204);
}
throw error;
}
};

const organizationLearnersController = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export const getAttestationZipForDivisions = async ({
return organizationLearnerRepository.getAttestationsForOrganizationLearnersAndKey({
attestationKey,
organizationLearners,
organizationId,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,17 @@ async function findOrganizationLearnersByDivisions({ organizationId, divisions }
return organizationLearners.map((organizationLearner) => new OrganizationLearner(organizationLearner));
}

async function getAttestationsForOrganizationLearnersAndKey({ attestationKey, organizationLearners, attestationsApi }) {
async function getAttestationsForOrganizationLearnersAndKey({
attestationKey,
organizationLearners,
organizationId,
attestationsApi,
}) {
const userIds = organizationLearners.map((learner) => learner.userId);
return attestationsApi.generateAttestations({
attestationKey,
userIds,
organizationId,
});
}

Expand Down
10 changes: 9 additions & 1 deletion api/src/profile/application/api/attestations-api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'node:path';
import * as url from 'node:url';

import { LOCALE } from '../../../shared/domain/constants.js';
import { usecases } from '../../domain/usecases/index.js';
import * as pdfWithFormSerializer from '../../infrastructure/serializers/pdf/pdf-with-form-serializer.js';

Expand All @@ -9,9 +10,16 @@ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
export const generateAttestations = async function ({
attestationKey,
userIds,
organizationId,
dependencies = { pdfWithFormSerializer },
}) {
const { data, templateName } = await usecases.getAttestationDataForUsers({ attestationKey, userIds });
const locale = LOCALE.FRENCH_FRANCE;
const { data, templateName } = await usecases.getSharedAttestationsForOrganizationByUserIds({
attestationKey,
userIds,
organizationId,
locale,
});

const templatePath = path.join(__dirname, `../../infrastructure/serializers/pdf/templates/${templateName}.pdf`);

Expand Down
13 changes: 12 additions & 1 deletion api/src/profile/domain/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ class ProfileRewardCantBeSharedError extends DomainError {
}
}

export { AttestationNotFoundError, ProfileRewardCantBeSharedError, RewardTypeDoesNotExistError };
class NoProfileRewardsFoundError extends DomainError {
constructor(message = 'No profile rewards found') {
super(message);
}
}

export {
AttestationNotFoundError,
NoProfileRewardsFoundError,
ProfileRewardCantBeSharedError,
RewardTypeDoesNotExistError,
};
8 changes: 8 additions & 0 deletions api/src/profile/domain/models/OrganizationProfileReward.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class OrganizationProfileReward {
constructor({ organizationId, profileRewardId }) {
this.profileRewardId = profileRewardId;
this.organizationId = organizationId;
}
}

export { OrganizationProfileReward };
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export async function getAttestationDataForUsers({
profileRewardRepository,
attestationRepository,
}) {
const users = await userRepository.getByIds({ userIds });
const profileRewards = await profileRewardRepository.getByAttestationKeyAndUserIds({ attestationKey, userIds });

const attestationData = await attestationRepository.getByKey({ attestationKey });

if (!attestationData) {
throw new AttestationNotFoundError();
}
const users = await userRepository.getByIds({ userIds });

const profileRewards = await profileRewardRepository.getByAttestationKeyAndUserIds({ attestationKey, userIds });

return {
data: profileRewards.map(({ userId, createdAt }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { AttestationNotFoundError, NoProfileRewardsFoundError } from '../errors.js';

export async function getSharedAttestationsForOrganizationByUserIds({
attestationKey,
userIds,
organizationId,
locale,
userRepository,
profileRewardRepository,
attestationRepository,
organizationProfileRewardRepository,
}) {
const attestationData = await attestationRepository.getByKey({ attestationKey });

if (!attestationData) {
throw new AttestationNotFoundError();
}

const users = await userRepository.getByIds({ userIds });

const sharedProfileRewards = await organizationProfileRewardRepository.getByOrganizationId({ organizationId });
const profileRewardIds = sharedProfileRewards.map((sharedProfileReward) => sharedProfileReward.profileRewardId);

const profileRewards = await profileRewardRepository.getByIds({ profileRewardIds });
const filteredProfileRewards = profileRewards.filter((profileReward) => userIds.includes(profileReward.userId));

if (filteredProfileRewards.length === 0) {
throw new NoProfileRewardsFoundError();
}

return {
data: filteredProfileRewards.map(({ userId, createdAt }) => {
const user = users.find((user) => user.id === userId);
return user.toForm(createdAt, locale);
}),
templateName: attestationData.templateName,
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ORGANIZATIONS_PROFILE_REWARDS_TABLE_NAME } from '../../../../db/migrations/20241118134739_create-organizations-profile-rewards-table.js';
import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js';
import { OrganizationProfileReward } from '../../domain/models/OrganizationProfileReward.js';

export const save = async ({ organizationId, profileRewardId }) => {
const knexConn = DomainTransaction.getConnection();
Expand All @@ -11,3 +12,11 @@ export const save = async ({ organizationId, profileRewardId }) => {
.onConflict()
.ignore();
};

export const getByOrganizationId = async ({ organizationId }) => {
const knexConn = DomainTransaction.getConnection();
const organizationProfileRewards = await knexConn(ORGANIZATIONS_PROFILE_REWARDS_TABLE_NAME).where({ organizationId });
return organizationProfileRewards.map(
(organizationProfileReward) => new OrganizationProfileReward(organizationProfileReward),
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ export const getById = async ({ profileRewardId }) => {
return profileReward ? toDomain(profileReward) : null;
};

/**
* @param {Object} args
* @param {number} args.profileRewardIds
* @returns {Promise<Array<ProfileReward>>}
*/
export const getByIds = async ({ profileRewardIds }) => {
const knexConnection = await DomainTransaction.getConnection();
const profileRewards = await knexConnection(PROFILE_REWARDS_TABLE_NAME).whereIn('id', profileRewardIds);

return profileRewards.map(toDomain);
};

/**
* @param {Object} args
* @param {string} args.attestationKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ describe('Prescription | Learner-Management | Unit | Infrastructure | organizati
const attestationKey = Symbol('attestation');
const userId1 = 1;
const userId2 = 2;
const organizationId = Symbol('organizationId');
const organizationLearners = [{ userId: userId1 }, { userId: userId2 }];

const expectedResult = Symbol('expectedResult');

attestationApiStub.generateAttestations
.withArgs({ attestationKey, userIds: [userId1, userId2] })
.withArgs({ attestationKey, userIds: [userId1, userId2], organizationId })
.resolves(expectedResult);

//when
const result = await getAttestationsForOrganizationLearnersAndKey({
attestationKey,
organizationLearners,
organizationId,
attestationsApi: attestationApiStub,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ describe('Prescription | Organization Learner | Acceptance | Application | Organ
organizationId,
division: '6emeA',
});
databaseBuilder.factory.buildProfileReward({
const profileRewardId = databaseBuilder.factory.buildProfileReward({
userId: organizationLearner.userId,
rewardId: attestation.id,
rewardType: REWARD_TYPES.ATTESTATION,
});
}).id;
databaseBuilder.factory.buildOrganizationsProfileRewards({ organizationId, profileRewardId });

await databaseBuilder.commit();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ import { databaseBuilder, expect } from '../../../../../test-helper.js';
describe('Integration | Prescription | Learner Management | Domain | UseCase | get-attestation-zip-for-divisions', function () {
it('returns a zip attestation', async function () {
// given
const templateName = 'sixth-grade-attestation-template';
const organizationId = databaseBuilder.factory.buildOrganization().id;
databaseBuilder.factory.buildOrganizationLearner({ organizationId, division: '6eme A' });
databaseBuilder.factory.buildOrganizationLearner({ organizationId, division: '6eme B' });
const attestation = databaseBuilder.factory.buildAttestation();

const firstLearner = databaseBuilder.factory.buildOrganizationLearner({ organizationId, division: '6eme A' });
const secondLearner = databaseBuilder.factory.buildOrganizationLearner({ organizationId, division: '6eme B' });
const attestation = databaseBuilder.factory.buildAttestation({ templateName });
const firstRewardId = databaseBuilder.factory.buildProfileReward({
rewardId: attestation.id,
userId: firstLearner.userId,
});
databaseBuilder.factory.buildProfileReward({ rewardId: attestation.id, userId: secondLearner.userId });
databaseBuilder.factory.buildOrganizationsProfileRewards({ organizationId, profileRewardId: firstRewardId.id });
await databaseBuilder.commit();

// when
Expand Down
Loading

0 comments on commit da12267

Please sign in to comment.