Skip to content

Commit

Permalink
[FEATURE] Ajouter un endpoint pour récuperer les statistiques par com…
Browse files Browse the repository at this point in the history
…pétences au grain d'une organisation (PIX-15451)

 #10676
  • Loading branch information
pix-service-auto-merge authored Nov 29, 2024
2 parents 8938b33 + 8eb1053 commit 696ca67
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ const getAttestationZipForDivisions = async function (request, h) {
}
};

const getAnalysisByTubes = async function (request, h) {
const organizationId = request.params.organizationId;
const result = await usecases.getAnalysisByTubes({ organizationId });
return h.response(result).code(200);
};

const organizationLearnersController = {
getAnalysisByTubes,
getAttestationZipForDivisions,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
import Joi from 'joi';

import { securityPreHandlers } from '../../../shared/application/security-pre-handlers.js';
import { ORGANIZATION_FEATURE } from '../../../shared/domain/constants.js';
import { identifiersType } from '../../../shared/domain/types/identifiers-type.js';
import { organizationLearnersController } from './organization-learners-controller.js';

const register = async function (server) {
server.route([
{
method: 'GET',
path: '/api/organizations/{organizationId}/organization-learners-level-by-tubes',
config: {
pre: [
{
method: securityPreHandlers.checkUserIsAdminInOrganization,
assign: 'checkUserIsAdminInOrganization',
},
{
method: securityPreHandlers.makeCheckOrganizationHasFeature(ORGANIZATION_FEATURE.COVER_RATE.key),
assign: 'makeCheckOrganizationHasFeature',
},
],
validate: {
params: Joi.object({
organizationId: identifiersType.organizationId,
}),
},
handler: organizationLearnersController.getAnalysisByTubes,
notes: [
'- **Cette route est restreinte aux utilisateurs authentifiés**\n' +
'- Cette route retourne le taux de couverture par tubes pour l\'organisation"- ' +
"- L'organisation doit avoir la feature COVER_RATE d'activée" +
"- L'utisateur doit être admin de l'organisation'",
],
tags: ['api', 'organization', 'analysis'],
},
},
{
method: 'GET',
path: '/api/organizations/{organizationId}/attestations/{attestationKey}',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const getAnalysisByTubes = async function ({ organizationId, analysisRepository }) {
return analysisRepository.findByTubes({ organizationId });
};

export { getAnalysisByTubes };
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import * as scoOrganizationParticipantRepository from '../../infrastructure/repo
import * as supOrganizationParticipantRepository from '../../infrastructure/repositories/sup-organization-participant-repository.js';

const dependencies = {
analysisRepository: repositories.analysisRepository,
divisionRepository,
groupRepository,
supOrganizationParticipantRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { config } from '../../../../shared/config.js';

async function findByTubes({ organizationId, apiDataDatasource }) {
return apiDataDatasource.get(config.apiData.queries.coverRateByTubes, [
{
name: 'organization_id',
value: organizationId,
},
]);
}

export { findByTubes };
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import * as attestationsApi from '../../../../profile/application/api/attestations-api.js';
import { apiData as apiDataDatasource } from '../../../../shared/infrastructure/datasources/ApiData.js';
import { injectDependencies } from '../../../../shared/infrastructure/utils/dependency-injection.js';
import * as analysisRepository from './analysis-repository.js';
import * as organizationLearnerRepository from './organization-learner-repository.js';

const repositoriesWithoutInjectedDependencies = {
analysisRepository,
organizationLearnerRepository,
};

const dependencies = {
apiDataDatasource,
attestationsApi,
};

Expand Down
2 changes: 1 addition & 1 deletion api/src/shared/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ const configuration = (function () {
password: 'passowrd',
},
queries: {
coverageRate: 'coverage-rate-query-id',
coverRateByTubes: 'coverage-rate-query-id',
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { REWARD_TYPES } from '../../../../../src/quest/domain/constants.js';
import { config } from '../../../../../src/shared/config.js';
import { ORGANIZATION_FEATURE } from '../../../../../src/shared/domain/constants.js';
import { Membership } from '../../../../../src/shared/domain/models/index.js';
import {
createServer,
databaseBuilder,
expect,
generateValidRequestAuthorizationHeader,
insertUserWithRoleSuperAdmin,
nock,
} from '../../../../test-helper.js';

describe('Prescription | Organization Learner | Acceptance | Application | OrganizationLearnerRoute', function () {
Expand Down Expand Up @@ -57,4 +60,57 @@ describe('Prescription | Organization Learner | Acceptance | Application | Organ
expect(response.headers['content-type']).to.equal('application/zip');
});
});

describe('GET /api/organizations/{organizationId}/organization-learners-level-by-tubes', function () {
describe('Success case', function () {
it('should return the organization learner and a 200 status code response', async function () {
//given
const userId = databaseBuilder.factory.buildUser().id;
const organizationId = databaseBuilder.factory.buildOrganization().id;
databaseBuilder.factory.buildMembership({
organizationId,
userId,
organizationRole: Membership.roles.ADMIN,
});
const featureId = databaseBuilder.factory.buildFeature(ORGANIZATION_FEATURE.COVER_RATE).id;
databaseBuilder.factory.buildOrganizationFeature({
organizationId,
featureId,
});
await databaseBuilder.commit();

const expectedData = 'expected-data';

const token = 'token';
nock(config.apiData.url)
.post('/token')
.reply(200, { test: 'test', data: { access_token: token } });

nock(config.apiData.url)
.post('/query', {
queryId: config.apiData.queries.coverRateByTubes,
params: [{ name: 'organization_id', value: organizationId }],
})
.matchHeader('Content-Type', 'application/json')
.matchHeader('Authorization', `Bearer ${token}`)
.reply(200, { status: 'success', data: expectedData });

const options = {
method: 'GET',
url: `/api/organizations/${organizationId}/organization-learners-level-by-tubes`,
headers: { authorization: generateValidRequestAuthorizationHeader(userId) },
};

// when
const response = await server.inject(options);

// then
expect(response.statusCode).to.equal(200);
expect(response.result).to.deep.equal({
data: expectedData,
status: 'success',
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as analysisRepository from '../../../../../../src/prescription/organization-learner/infrastructure/repositories/analysis-repository.js';
import { config } from '../../../../../../src/shared/config.js';
import { expect, sinon } from '../../../../../test-helper.js';

describe('Integration | Infrastructure | Repository | Analysis', function () {
describe('#findByTubes', function () {
context('when they work properly', function () {
it('should call api data and return it', async function () {
const organizationId = Symbol('organization-id');
const apiDataDatasource = {
get: sinon.stub(),
};

sinon.stub(config, 'apiData').value({
queries: {
coverRateByTubes: Symbol('cover-query-id'),
},
});

const queryId = config.apiData.queries.coverRateByTubes;
const params = [{ name: 'organization_id', value: organizationId }];

const expectedData = Symbol('api-data-result');
apiDataDatasource.get.withArgs(queryId, params).resolves(expectedData);

const result = await analysisRepository.findByTubes({ organizationId, apiDataDatasource });

expect(result).to.equal(expectedData);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,26 @@ describe('Unit | Application | Organization-Learner | organization-learners-cont
});
});
});

describe('#getLearnersLevelsByTubes', function () {
it('should return data', async function () {
// given
const organizationId = 123;

sinon.stub(usecases, 'getAnalysisByTubes');
usecases.getAnalysisByTubes.withArgs({ organizationId }).resolves();

const request = {
params: {
organizationId,
},
};

// when
const response = await organizationLearnersController.getAnalysisByTubes(request, hFake);

// then
expect(response.statusCode).to.equal(200);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { organizationLearnersController } from '../../../../../src/prescription/organization-learner/application/organization-learners-controller.js';
import * as moduleUnderTest from '../../../../../src/prescription/organization-learner/application/organization-learners-route.js';
import { securityPreHandlers } from '../../../../../src/shared/application/security-pre-handlers.js';
import { ORGANIZATION_FEATURE } from '../../../../../src/shared/domain/constants.js';
import { expect, HttpTestServer, sinon } from '../../../../test-helper.js';

describe('Prescription | Unit | Router | organization-learner-router', function () {
describe('GET /api/organizations/{organizationId}/organization-learners-level-by-tubes', function () {
const method = 'GET';

it('should verify user privilege and organization feature access', async function () {
// given
sinon
.stub(organizationLearnersController, 'getAnalysisByTubes')
.callsFake((request, h) => h.response('ok').code(200));
sinon.stub(securityPreHandlers, 'checkUserIsAdminInOrganization').callsFake((request, h) => h.response(true));
sinon
.stub(securityPreHandlers, 'makeCheckOrganizationHasFeature')
.callsFake(() => (request, h) => h.response(true));

const httpTestServer = new HttpTestServer();
await httpTestServer.register(moduleUnderTest);

const url = '/api/organizations/123/organization-learners-level-by-tubes';

// when
const response = await httpTestServer.request(method, url);

// then
expect(response.statusCode).to.equal(200);
expect(securityPreHandlers.checkUserIsAdminInOrganization).to.have.been.calledBefore(
organizationLearnersController.getAnalysisByTubes,
);
expect(securityPreHandlers.makeCheckOrganizationHasFeature).calledWithExactly(
ORGANIZATION_FEATURE.COVER_RATE.key,
);
expect(securityPreHandlers.makeCheckOrganizationHasFeature).to.have.been.calledBefore(
organizationLearnersController.getAnalysisByTubes,
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getAnalysisByTubes } from '../../../../../../src/prescription/organization-learner/domain/usecases/get-analysis-by-tubes.js';
import { expect, sinon } from '../../../../../test-helper.js';

describe('Unit | UseCase | get-analysis-by-tubes', function () {
it('should call findByTubes method from analysis repository', async function () {
// given
const organizationId = Symbol('organizationId');
const expectedRepositoryResult = Symbol('expectedRepositoryResult');
const analysisRepository = {
findByTubes: sinon.stub(),
};

analysisRepository.findByTubes.withArgs({ organizationId }).resolves(expectedRepositoryResult);

// when
await getAnalysisByTubes({
organizationId,
analysisRepository,
});

// then
expect(analysisRepository.findByTubes).to.have.been.called;
});
});

0 comments on commit 696ca67

Please sign in to comment.