Skip to content

Commit

Permalink
[FEATURE] Ajout d'une route pour récupérer les infos de début de parc…
Browse files Browse the repository at this point in the history
…ours (PIX-14813).

 #10352
  • Loading branch information
pix-service-auto-merge authored Oct 23, 2024
2 parents ebf5209 + 5e4c6bc commit 239e83d
Show file tree
Hide file tree
Showing 10 changed files with 522 additions and 1 deletion.
15 changes: 15 additions & 0 deletions api/src/prescription/campaign/application/campaign-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as campaignAnalysisSerializer from '../../campaign-participation/infras
import { usecases } from '../domain/usecases/index.js';
import * as divisionSerializer from '../infrastructure/serializers/jsonapi/division-serializer.js';
import * as groupSerializer from '../infrastructure/serializers/jsonapi/group-serializer.js';
import * as presentationStepsSerializer from '../infrastructure/serializers/jsonapi/presentation-steps-serializer.js';

const division = async function (request) {
const { userId } = request.auth.credentials;
Expand All @@ -28,10 +29,24 @@ const getAnalysis = async function (request, h, dependencies = { campaignAnalysi
return dependencies.campaignAnalysisSerializer.serialize(campaignAnalysis);
};

const getPresentationSteps = async function (
request,
_,
dependencies = { presentationStepsSerializer, extractLocaleFromRequest },
) {
const { userId } = request.auth.credentials;
const campaignCode = request.params.campaignCode;
const locale = dependencies.extractLocaleFromRequest(request);

const presentationSteps = await usecases.getPresentationSteps({ userId, campaignCode, locale });
return dependencies.presentationStepsSerializer.serialize(presentationSteps);
};

const campaignController = {
division,
getAnalysis,
getGroups,
getPresentationSteps,
};

export { campaignController };
18 changes: 18 additions & 0 deletions api/src/prescription/campaign/application/campaign-route.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Joi from 'joi';

import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { identifiersType as prescriptionIdentifiersType } from '../../shared/domain/types/identifiers-type.js';
import { campaignController } from './campaign-controller.js';

const register = async function (server) {
Expand Down Expand Up @@ -56,6 +57,23 @@ const register = async function (server) {
tags: ['api', 'campaign'],
},
},
{
method: 'GET',
path: '/api/campaigns/{campaignCode}/presentation-steps',
config: {
handler: campaignController.getPresentationSteps,
validate: {
params: Joi.object({
campaignCode: prescriptionIdentifiersType.campaignCode,
}),
},
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Récupération des infos des écrans de présentation de début de parcours',
],
tags: ['api', 'campaign', 'presentation'],
},
},
]);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CampaignCodeError, UserNotAuthorizedToAccessEntityError } from '../../../../shared/domain/errors.js';
import { ArchivedCampaignError, DeletedCampaignError } from '../errors.js';

const getPresentationSteps = async function ({
userId,
campaignCode,
locale,
badgeRepository,
campaignRepository,
learningContentRepository,
}) {
const campaign = await campaignRepository.getByCode(campaignCode);

if (!campaign) throw new CampaignCodeError();
if (campaign.archivedAt) throw new ArchivedCampaignError();
if (campaign.deletedAt) throw new DeletedCampaignError();

const hasUserAccessToResult = await campaignRepository.checkIfUserOrganizationHasAccessToCampaign(
campaign.id,
userId,
);
if (!hasUserAccessToResult)
throw new UserNotAuthorizedToAccessEntityError('User does not have access to this campaign');

const campaignBadges = await badgeRepository.findByCampaignId(campaign.id);
const learningContent = await learningContentRepository.findByCampaignId(campaign.id, locale);

return {
customLandingPageText: campaign.customLandingPageText,
badges: campaignBadges,
competences: learningContent?.competences,
};
};

export { getPresentationSteps };
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import jsonapiSerializer from 'jsonapi-serializer';

const { Serializer } = jsonapiSerializer;

const serialize = function (presentationSteps) {
return new Serializer('campaign-presentation-step', {
attributes: ['customLandingPageText', 'badges', 'competences'],
badges: {
ref: 'id',
attributes: ['altMessage', 'imageUrl', 'isAlwaysVisible', 'isCertifiable', 'key', 'message', 'title'],
},
competences: {
ref: 'id',
attributes: ['index', 'name'],
},
}).serialize(presentationSteps);
};

export { serialize };
6 changes: 5 additions & 1 deletion api/src/prescription/shared/domain/types/identifiers-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import Joi from 'joi';
import { categories } from '../../../../shared/domain/models/TargetProfile.js';
import { certificabilityByLabel } from '../../application/helpers.js';

const identifiersType = {
campaignCode: Joi.string().min(9).max(9).required(),
};

const filterType = {
certificability: Joi.string().valid(...Object.keys(certificabilityByLabel)),
targetProfileCategory: Joi.string().valid(...Object.values(categories)),
};

export { filterType };
export { filterType, identifiersType };
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Membership } from '../../../../../src/shared/domain/models/Membership.j
import {
createServer,
databaseBuilder,
domainBuilder,
expect,
generateValidRequestAuthorizationHeader,
learningContentBuilder,
Expand Down Expand Up @@ -350,4 +351,96 @@ describe('Acceptance | API | Campaign Route', function () {
});
});
});

describe('GET /api/campaigns/{campaignId}/presentation-steps', function () {
it('should return the presentation steps informations', async function () {
// given
const userId = databaseBuilder.factory.buildUser().id;
const organization = databaseBuilder.factory.buildOrganization();

databaseBuilder.factory.buildMembership({
userId,
organizationId: organization.id,
organizationRole: Membership.roles.MEMBER,
});

const targetProfile = databaseBuilder.factory.buildTargetProfile({ organizationId: organization.id });
const badge = databaseBuilder.factory.buildBadge({ targetProfileId: targetProfile.id });
const campaign = databaseBuilder.factory.buildCampaign({
code: 'CAMPAIGN1',
customLandingPageText: 'landing page text',
targetProfileId: targetProfile.id,
organizationId: organization.id,
});

const learningContent = domainBuilder.buildLearningContent.withSimpleContent();
const learningContentObjects = learningContentBuilder.fromAreas(learningContent.frameworks[0].areas);
mockLearningContent(learningContentObjects);

databaseBuilder.factory.buildCampaignSkill({
campaignId: campaign.id,
skillId: learningContentObjects.competences[0].skillIds[0],
});
await databaseBuilder.commit();

// when
const options = {
method: 'GET',
url: `/api/campaigns/${campaign.code}/presentation-steps`,
headers: { authorization: generateValidRequestAuthorizationHeader(userId) },
};

const response = await server.inject(options);

// then
expect(response.statusCode).to.equal(200);
expect(response.result).to.deep.equal({
data: {
type: 'campaign-presentation-steps',
attributes: { 'custom-landing-page-text': campaign.customLandingPageText },
relationships: {
badges: {
data: [
{
id: String(badge.id),
type: 'badges',
},
],
},
competences: {
data: [
{
id: learningContentObjects.competences[0].id,
type: 'competences',
},
],
},
},
},
included: [
{
type: 'badges',
id: String(badge.id),
attributes: {
'alt-message': badge.altMessage,
'image-url': badge.imageUrl,
'is-always-visible': badge.isAlwaysVisible,
'is-certifiable': badge.isCertifiable,
key: badge.key,
message: badge.message,
title: badge.title,
},
},
{
type: 'competences',
id: learningContentObjects.competences[0].id,
attributes: {
index: learningContentObjects.competences[0].index,
name: learningContentObjects.competences[0].name,
},
},
],
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { usecases } from '../../../../../../src/prescription/campaign/domain/usecases/index.js';
import { LOCALE } from '../../../../../../src/shared/domain/constants.js';
import {
databaseBuilder,
domainBuilder,
expect,
learningContentBuilder,
mockLearningContent,
} from '../../../../../test-helper.js';

const { FRENCH_SPOKEN } = LOCALE;

describe('Integration | Campaign | UseCase | get-presentation-steps', function () {
let user, campaign, badges, competences;

beforeEach(async function () {
const learningContent = domainBuilder.buildLearningContent.withSimpleContent();
const learningContentObjects = learningContentBuilder.fromAreas(learningContent.frameworks[0].areas);
mockLearningContent(learningContentObjects);

const targetProfileId = databaseBuilder.factory.buildTargetProfile().id;

campaign = databaseBuilder.factory.buildCampaign({ targetProfileId });

user = databaseBuilder.factory.buildUser.withMembership({ organizationId: campaign.organizationId });

badges = [
databaseBuilder.factory.buildBadge({ targetProfileId }),
databaseBuilder.factory.buildBadge({ targetProfileId }),
];

competences = learningContentObjects.competences;

databaseBuilder.factory.buildCampaignSkill({
campaignId: campaign.id,
skillId: competences[0].skillIds[0],
});

await databaseBuilder.commit();
});

it('should get campaign presentation steps content', async function () {
// when
const result = await usecases.getPresentationSteps({
userId: user.id,
campaignCode: campaign.code,
locale: FRENCH_SPOKEN,
});

// then
expect(result.customLandingPageText).to.equal(campaign.customLandingPageText);
expect(result.badges).to.deep.equal(badges);
expect(result.competences).to.have.lengthOf(competences.length);
expect(result.competences[0].id).to.equal(competences[0].id);
expect(result.competences[0].index).to.equal(competences[0].index);
expect(result.competences[0].name).to.equal(competences[0].name);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,43 @@ describe('Unit | Application | Controller | Campaign', function () {
expect(errorCatched).to.be.instanceof(UserNotAuthorizedToAccessEntityError);
});
});

describe('#getPresentationSteps', function () {
it('should return expected results', async function () {
// given
const userId = Symbol('userId');
const campaignCode = Symbol('campaign code');
const locale = FRENCH_SPOKEN;

sinon.stub(usecases, 'getPresentationSteps');

const dependencies = {
extractLocaleFromRequestStub: sinon.stub().returns(locale),
presentationStepsSerializerStub: {
serialize: sinon.stub(),
},
};

const presentationSteps = Symbol('presentation steps');
const expectedResults = Symbol('results');

usecases.getPresentationSteps.withArgs({ userId, campaignCode, locale }).resolves(presentationSteps);
dependencies.presentationStepsSerializerStub.serialize.withArgs(presentationSteps).returns(expectedResults);

const request = {
auth: { credentials: { userId } },
params: { campaignCode },
headers: { 'accept-language': locale },
};

// when
const response = await campaignController.getPresentationSteps(request, hFake, {
extractLocaleFromRequest: dependencies.extractLocaleFromRequestStub,
presentationStepsSerializer: dependencies.presentationStepsSerializerStub,
});

// then
expect(response).to.equal(expectedResults);
});
});
});
Loading

0 comments on commit 239e83d

Please sign in to comment.