Skip to content

Commit

Permalink
[BUGIFX] Corriger le téléchargement du profil cible lors du télécharg…
Browse files Browse the repository at this point in the history
…ement du PDF (PIX-10009)

 #7490
  • Loading branch information
pix-service-auto-merge authored Nov 23, 2023
2 parents 750291b + ba7875a commit 9d2785a
Show file tree
Hide file tree
Showing 61 changed files with 845 additions and 510 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default class PdfParametersModal extends Component {
}

this.errorMessage = null;
this.args.onDownloadButtonClicked(this.language, this.args.name);
this.args.onDownloadButtonClicked(this.language);
}

@action
Expand Down
1 change: 0 additions & 1 deletion admin/app/components/target-profiles/target-profile.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,5 @@
@onDownloadButtonClicked={{this.downloadPDF}}
@onCloseButtonClicked={{this.toggleDisplayPdfParametersModal}}
@displayModal={{this.displayPdfParametersModal}}
@name={{@model.name}}
/>
</main>
4 changes: 2 additions & 2 deletions admin/app/components/target-profiles/target-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ export default class TargetProfile extends Component {
}

@action
async downloadPDF(language, title) {
async downloadPDF(language) {
try {
this.toggleDisplayPdfParametersModal();
const targetProfileId = this.args.model.id;
const url = `/api/admin/target-profiles/${targetProfileId}/learning-content-pdf?language=${language}&title=${title}`;
const url = `/api/admin/target-profiles/${targetProfileId}/learning-content-pdf?language=${language}`;
const fileName = 'whatever.pdf';
const token = this.session.data.authenticated.access_token;
await this.fileSaver.save({ url, fileName, token });
Expand Down
50 changes: 0 additions & 50 deletions api/lib/application/target-profiles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,6 @@ const register = async function (server) {
],
},
},
{
method: 'GET',
path: '/api/admin/target-profiles/{id}/content-json',
config: {
auth: false,
validate: {
params: Joi.object({
id: identifiersType.targetProfileId,
}),
},
handler: targetProfileController.getContentAsJsonFile,
tags: ['api', 'admin', 'target-profiles', 'json'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
'- Elle permet de récupérer le profil cible dans un fichier json',
],
},
},
{
method: 'GET',
path: '/api/admin/target-profiles/{id}/training-summaries',
Expand Down Expand Up @@ -410,38 +392,6 @@ const register = async function (server) {
],
},
},
{
method: 'GET',
path: '/api/admin/target-profiles/{id}/learning-content-pdf',
config: {
pre: [
{
method: (request, h) =>
securityPreHandlers.adminMemberHasAtLeastOneAccessOf([
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
securityPreHandlers.checkAdminMemberHasRoleSupport,
securityPreHandlers.checkAdminMemberHasRoleMetier,
])(request, h),
assign: 'hasAuthorizationToAccessAdminScope',
},
],
validate: {
params: Joi.object({
id: identifiersType.targetProfileId,
}),
query: Joi.object({
language: Joi.string().valid('fr', 'en').required(),
title: Joi.string().required(),
}),
},
handler: targetProfileController.getLearningContentAsPdf,
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
'- Elle permet de récupérer le référentiel du profil cible en version pdf',
],
tags: ['api', 'learning-content', 'target-profile', 'PDF'],
},
},
]);
};

Expand Down
37 changes: 0 additions & 37 deletions api/lib/application/target-profiles/target-profile-controller.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { usecases } from '../../domain/usecases/index.js';
import { tokenService } from '../../../src/shared/domain/services/token-service.js';
import * as targetProfileSerializer from '../../infrastructure/serializers/jsonapi/target-profile-serializer.js';
import * as targetProfileSummaryForAdminSerializer from '../../infrastructure/serializers/jsonapi/target-profile-summary-for-admin-serializer.js';
import * as targetProfileForAdminSerializer from '../../infrastructure/serializers/jsonapi/target-profile-for-admin-serializer.js';
import * as queryParamsUtils from '../../infrastructure/utils/query-params-utils.js';
import { escapeFileName } from '../../infrastructure/utils/request-response-utils.js';
import * as organizationSerializer from '../../infrastructure/serializers/jsonapi/organization-serializer.js';
import * as badgeSerializer from '../../infrastructure/serializers/jsonapi/badge-serializer.js';
import { deserializer as badgeCreationDeserializer } from '../../infrastructure/serializers/jsonapi/badge-creation-serializer.js';
import * as targetProfileAttachOrganizationSerializer from '../../infrastructure/serializers/jsonapi/target-profile-attach-organization-serializer.js';
import * as learningContentPDFPresenter from './presenter/pdf/learning-content-pdf-presenter.js';
import { DomainTransaction } from '../../infrastructure/DomainTransaction.js';
import * as trainingSummarySerializer from '../../infrastructure/serializers/jsonapi/training-summary-serializer.js';

Expand Down Expand Up @@ -41,20 +38,6 @@ const findPaginatedFilteredTargetProfileOrganizations = async function (request)
return organizationSerializer.serialize(organizations, pagination);
};

const getContentAsJsonFile = async function (request, h, dependencies = { tokenService }) {
const targetProfileId = request.params.id;
const token = request.query.accessToken;
const userId = dependencies.tokenService.extractUserId(token);

const { jsonContent, fileName } = await usecases.getTargetProfileContentAsJson({ userId, targetProfileId });
const escapedFilename = escapeFileName(fileName);

return h
.response(jsonContent)
.header('Content-Type', 'text/json;charset=utf-8')
.header('Content-Disposition', `attachment; filename=${escapedFilename}`);
};

const attachOrganizations = async function (request, h, dependencies = { targetProfileAttachOrganizationSerializer }) {
const organizationIds = request.payload['organization-ids'];
const targetProfileId = request.params.id;
Expand Down Expand Up @@ -144,29 +127,10 @@ const markTargetProfileAsSimplifiedAccess = async function (request, h) {
return h.response(targetProfileSerializer.serialize(targetProfile));
};

const getLearningContentAsPdf = async function (request, h, dependencies = { learningContentPDFPresenter }) {
const targetProfileId = request.params.id;
const { title, language } = request.query;

const learningContent = await usecases.getLearningContentByTargetProfile({ targetProfileId, language });
const pdfBuffer = await dependencies.learningContentPDFPresenter.present(learningContent, title, language);

const date = new Date();
const dateString = date.getDate() + '-' + (date.getMonth() + 1) + '-' + date.getFullYear();
const timeString = date.getHours() + '-' + date.getMinutes();
const fileName = `${title}_${dateString}_${timeString}.pdf`;

return h
.response(pdfBuffer)
.header('Content-Disposition', `attachment; filename=${fileName}`)
.header('Content-Type', 'application/pdf');
};

const targetProfileController = {
findPaginatedFilteredTargetProfileSummariesForAdmin,
getTargetProfileForAdmin,
findPaginatedFilteredTargetProfileOrganizations,
getContentAsJsonFile,
attachOrganizations,
attachOrganizationsFromExistingTargetProfile,
updateTargetProfile,
Expand All @@ -175,7 +139,6 @@ const targetProfileController = {
findPaginatedTrainings,
createBadge,
markTargetProfileAsSimplifiedAccess,
getLearningContentAsPdf,
};

export { targetProfileController };

This file was deleted.

2 changes: 1 addition & 1 deletion api/lib/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ import * as studentRepository from '../../infrastructure/repositories/student-re
import * as supervisorAccessRepository from '../../infrastructure/repositories/supervisor-access-repository.js';
import * as supOrganizationLearnerRepository from '../../../src/prescription/learner-management/infrastructure/repositories/sup-organization-learner-repository.js';
import * as tagRepository from '../../infrastructure/repositories/tag-repository.js';
import * as targetProfileForAdminRepository from '../../infrastructure/repositories/target-profile-for-admin-repository.js';
import * as targetProfileForAdminRepository from '../../../src/shared/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as TargetProfileForSpecifierRepository from '../../infrastructure/repositories/campaign/target-profile-for-specifier-repository.js';
import * as targetProfileForUpdateRepository from '../../infrastructure/repositories/target-profile-for-update-repository.js';
import * as targetProfileRepository from '../../infrastructure/repositories/target-profile-repository.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { logger } from '../../../lib/infrastructure/logger.js';
import { learningContentCache as cache } from '../../../lib/infrastructure/caches/learning-content-cache.js';
import { disconnect } from '../../../db/knex-database-connection.js';

import * as targetProfileForAdminRepository from '../../../lib/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as targetProfileForAdminRepository from '../../../src/shared/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as skillRepository from '../../../lib/infrastructure/repositories/skill-repository.js';
import * as organizationRepository from '../../../lib/infrastructure/repositories/organization-repository.js';
import * as stageCollectionRepository from '../../../src/evaluation/infrastructure/repositories/stage-collection-repository.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { logger } from '../../../lib/infrastructure/logger.js';
import { learningContentCache as cache } from '../../../lib/infrastructure/caches/learning-content-cache.js';
import { disconnect } from '../../../db/knex-database-connection.js';

import * as targetProfileForAdminRepository from '../../../lib/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as targetProfileForAdminRepository from '../../../src/shared/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as stageCollectionRepository from '../../../src/evaluation/infrastructure/repositories/stage-collection-repository.js';

set_fs(fs);
Expand Down
3 changes: 2 additions & 1 deletion api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from './src/certification/complementary-certification/routes.js';
import { learnerManagementRoutes } from './src/prescription/learner-management/routes.js';
import { learnerListRoutes } from './src/prescription/organization-learner/routes.js';
import { targetProfileRoutes } from './src/prescription/target-profile/routes.js';
import { prescriberManagementRoutes } from './src/shared/prescriber-management/routes.js';
import { devcompRoutes } from './src/devcomp/routes.js';
import { schoolRoutes } from './src/school/routes.js';
Expand All @@ -35,7 +36,7 @@ const certificationRoutes = [
complementaryCertificationRoutes,
certificationCourseRoutes,
];
const prescriptionRoutes = [learnerManagementRoutes, learnerListRoutes];
const prescriptionRoutes = [learnerManagementRoutes, learnerListRoutes, targetProfileRoutes];
const sharedRoutes = [prescriberManagementRoutes];

monitoringTools.installHapiHook();
Expand Down
2 changes: 1 addition & 1 deletion api/src/evaluation/domain/usecases/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import * as stageAcquisitionRepository from '../../infrastructure/repositories/s
import * as stageCollectionForTargetProfileRepository from '../../infrastructure/repositories/stage-collection-repository.js';
import * as stageRepository from '../../infrastructure/repositories/stage-repository.js';
import * as feedbackRepository from '../../infrastructure/repositories/feedback-repository.js';
import * as targetProfileForAdminRepository from '../../../../lib/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as targetProfileRepository from '../../../../lib/infrastructure/repositories/target-profile-repository.js';
import * as targetProfileForAdminRepository from '../../../shared/infrastructure/repositories/target-profile-for-admin-repository.js';
import { getCompetenceLevel } from '../services/get-competence-level.js';
import * as scorecardService from '../services/scorecard-service.js';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { usecases } from '../domain/usecases/index.js';
import { tokenService } from '../../../shared/domain/services/token-service.js';
import { escapeFileName } from '../../../../lib/infrastructure/utils/request-response-utils.js';
import * as learningContentPDFPresenter from './presenter/pdf/learning-content-pdf-presenter.js';
import dayjs from 'dayjs';

const getContentAsJsonFile = async function (request, h, dependencies = { tokenService }) {
const targetProfileId = request.params.id;
const token = request.query.accessToken;
const userId = dependencies.tokenService.extractUserId(token);

const { jsonContent, fileName } = await usecases.getTargetProfileContentAsJson({ userId, targetProfileId });
const escapedFilename = escapeFileName(fileName);

return h
.response(jsonContent)
.header('Content-Type', 'text/json;charset=utf-8')
.header('Content-Disposition', `attachment; filename=${escapedFilename}`);
};

const getLearningContentAsPdf = async function (request, h, dependencies = { learningContentPDFPresenter }) {
const targetProfileId = request.params.id;
const { language } = request.query;

const { learningContent, targetProfileName } = await usecases.getLearningContentByTargetProfile({
targetProfileId,
language,
});

const filename = `${dayjs().format('YYYYMMDD')}_profil_cible_${targetProfileName}.pdf`;

const pdfBuffer = await dependencies.learningContentPDFPresenter.present(
learningContent,
targetProfileName,
language,
);

return h
.response(pdfBuffer)
.header('Content-Disposition', `attachment; filename=${filename}`)
.header('Content-Type', 'application/pdf');
};

const targetProfileController = {
getContentAsJsonFile,
getLearningContentAsPdf,
};

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

import { securityPreHandlers } from '../../../../lib/application/security-pre-handlers.js';
import { targetProfileController } from './admin-target-profile-controller.js';
import { identifiersType } from '../../../../lib/domain/types/identifiers-type.js';

const register = async function (server) {
server.route([
{
method: 'GET',
path: '/api/admin/target-profiles/{id}/learning-content-pdf',
config: {
pre: [
{
method: (request, h) =>
securityPreHandlers.adminMemberHasAtLeastOneAccessOf([
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
securityPreHandlers.checkAdminMemberHasRoleSupport,
securityPreHandlers.checkAdminMemberHasRoleMetier,
])(request, h),
assign: 'hasAuthorizationToAccessAdminScope',
},
],
validate: {
params: Joi.object({
id: identifiersType.targetProfileId,
}),
query: Joi.object({
language: Joi.string().valid('fr', 'en').required(),
}),
},
handler: targetProfileController.getLearningContentAsPdf,
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
'- Elle permet de récupérer le référentiel du profil cible en version pdf',
],
tags: ['api', 'learning-content', 'target-profile', 'PDF'],
},
},
{
method: 'GET',
path: '/api/admin/target-profiles/{id}/content-json',
config: {
auth: false,
validate: {
params: Joi.object({
id: identifiersType.targetProfileId,
}),
},
handler: targetProfileController.getContentAsJsonFile,
tags: ['api', 'admin', 'target-profiles', 'json'],
notes: [
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
'- Elle permet de récupérer le profil cible dans un fichier json',
],
},
},
]);
};

const name = 'admin-target-profiles-api';
export { register, name };
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const getLearningContentByTargetProfile = async function ({
targetProfileId,
language,
learningContentRepository,
targetProfileForAdminRepository,
}) {
const targetProfileForAdmin = await targetProfileForAdminRepository.get({ id: targetProfileId });
const learningContent = await learningContentRepository.findByTargetProfileId(targetProfileId, language);
return { learningContent, targetProfileName: targetProfileForAdmin.name };
};

export { getLearningContentByTargetProfile };
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dayjs from 'dayjs';
import { ForbiddenAccess } from '../../../src/shared/domain/errors.js';
import { ForbiddenAccess } from '../../../../shared/domain/errors.js';

const getTargetProfileContentAsJson = async function ({
userId,
Expand Down
29 changes: 29 additions & 0 deletions api/src/prescription/target-profile/domain/usecases/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

import * as targetProfileForAdminRepository from '../../../../shared/infrastructure/repositories/target-profile-for-admin-repository.js';
import * as learningContentConversionService from '../../../../../lib/domain/services/learning-content/learning-content-conversion-service.js';
import * as learningContentRepository from '../../../../../lib/infrastructure/repositories/learning-content-repository.js';
import * as adminMemberRepository from '../../../../shared/infrastructure/repositories/admin-member-repository.js';
import { importNamedExportsFromDirectory } from '../../../../shared/infrastructure/utils/import-named-exports-from-directory.js';
import { injectDependencies } from '../../../../shared/infrastructure/utils/dependency-injection.js';

const dependencies = {
targetProfileForAdminRepository,
learningContentConversionService,
learningContentRepository,
adminMemberRepository,
};

const path = dirname(fileURLToPath(import.meta.url));

const usecasesWithoutInjectedDependencies = {
...(await importNamedExportsFromDirectory({
path: join(path, './'),
ignoredFileNames: ['index.js'],
})),
};

const usecases = injectDependencies(usecasesWithoutInjectedDependencies, dependencies);

export { usecases };
5 changes: 5 additions & 0 deletions api/src/prescription/target-profile/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as adminTargetProfileRoutes from './application/admin-target-profile-route.js';

const targetProfileRoutes = [adminTargetProfileRoutes];

export { targetProfileRoutes };
Loading

0 comments on commit 9d2785a

Please sign in to comment.