Skip to content

Commit

Permalink
[TECH] Export de la liste blanche SCO
Browse files Browse the repository at this point in the history
  • Loading branch information
pix-service-auto-merge authored Nov 27, 2024
2 parents 96afecd + 4f2b6f4 commit 04a614e
Show file tree
Hide file tree
Showing 25 changed files with 590 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import PixButton from '@1024pix/pix-ui/components/pix-button';
import PixButtonUpload from '@1024pix/pix-ui/components/pix-button-upload';
import PixMessage from '@1024pix/pix-ui/components/pix-message';
import { action } from '@ember/object';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { t } from 'ember-intl';
import ENV from 'pix-admin/config/environment';

Expand All @@ -12,6 +14,9 @@ export default class ScoWhitelistConfiguration extends Component {
@service intl;
@service session;
@service pixToast;
@service fileSaver;

@tracked isExportLoading = false;

@action
async importScoWhitelist(files) {
Expand All @@ -33,31 +38,57 @@ export default class ScoWhitelistConfiguration extends Component {
message: this.intl.t('pages.administration.certification.sco-whitelist.import.success'),
});
} else {
const responseJson = await response.json();
const errorKey = responseJson?.errors[0]?.code || 'error';

this.pixToast.sendErrorNotification({
message: this.intl.t('pages.administration.certification.sco-whitelist.import.error'),
message: this.intl.t(`pages.administration.certification.sco-whitelist.import.${errorKey}`),
});
}
} catch (error) {
this.pixToast.sendErrorNotification({
message: this.intl.t('pages.administration.certification.sco-whitelist.import.error'),
});
}
}

@action
async exportWhitelist() {
try {
this.isExportLoading = true;
const url = `${ENV.APP.API_HOST}/api/admin/sco-whitelist`;
const fileName = 'sco-whitelist.csv';
const token = this.session.data.authenticated.access_token;
await this.fileSaver.save({ url, fileName, token });
} catch (error) {
this.pixToast.sendErrorNotification({
message: this.intl.t('pages.administration.certification.sco-whitelist.export.error'),
});
} finally {
this.isLoading = false;
this.isExportLoading = false;
}
}

<template>
<AdministrationBlockLayout @title={{t "pages.administration.certification.sco-whitelist.title"}}>
<PixMessage @type="warning">Feature en cours de construction</PixMessage>
<br />
<PixButtonUpload
@id="sco-whitelist-file-upload"
@onChange={{this.importScoWhitelist}}
@variant="secondary"
accept=".csv"
>
{{t "pages.administration.certification.sco-whitelist.import.button"}}
</PixButtonUpload>
<AdministrationBlockLayout
@title={{t "pages.administration.certification.sco-whitelist.title"}}
class="sco-whitelist-configuration"
>
<PixMessage @type="info">{{t "pages.administration.certification.sco-whitelist.instructions"}}</PixMessage>

<div class="sco-whitelist-configuration__actions">
<PixButton @triggerAction={{this.exportWhitelist}} @isLoading={{this.isExportLoading}}>
{{t "pages.administration.certification.sco-whitelist.export.button"}}
</PixButton>
<PixButtonUpload
@id="sco-whitelist-file-upload"
@onChange={{this.importScoWhitelist}}
@variant="secondary"
accept=".csv"
>
{{t "pages.administration.certification.sco-whitelist.import.button"}}
</PixButtonUpload>
</div>
</AdministrationBlockLayout>
</template>
}
1 change: 1 addition & 0 deletions admin/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
@import 'components/administration/certification/certification-scoring-configuration';
@import 'components/administration/certification/competence-scoring-configuration';
@import 'components/administration/certification/flash-algorithm-configuration-form';
@import 'components/administration/certification/sco-whitelist-configuration';
@import 'components/administration/certification/scoring-simulator';
@import 'components/autonomous-courses/details';
@import 'components/autonomous-courses/form';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.sco-whitelist-configuration {
&__actions {
display: flex;
flex-direction: row;
gap: var(--pix-spacing-6x);
margin-top: var(--pix-spacing-6x);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,58 @@ module('Integration | Component | administration/certification/sco-whitelist-con
});

module('when import fails', function () {
test('it displays an error notification', async function (assert) {
// given
fetchStub
.withArgs(`${ENV.APP.API_HOST}/api/admin/sco-whitelist`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'text/csv',
Accept: 'application/json',
},
method: 'POST',
body: file,
})
.rejects();
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByLabelText(t('pages.administration.certification.sco-whitelist.import.button'));
await triggerEvent(input, 'change', { files: [file] });
module('when it is a generic error', function () {
test('it displays an error notification', async function (assert) {
// given
fetchStub
.withArgs(`${ENV.APP.API_HOST}/api/admin/sco-whitelist`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'text/csv',
Accept: 'application/json',
},
method: 'POST',
body: file,
})
.rejects();
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByLabelText(t('pages.administration.certification.sco-whitelist.import.button'));
await triggerEvent(input, 'change', { files: [file] });

// then
assert.ok(await screen.findByText(t('pages.administration.certification.sco-whitelist.import.error')));
// then
assert.ok(await screen.findByText(t('pages.administration.certification.sco-whitelist.import.error')));
});
});

module('when it is a CERTIFICATION_INVALID_SCO_WHITELIST_ERROR', function () {
test('it displays an error notification', async function (assert) {
// given
fetchStub
.withArgs(`${ENV.APP.API_HOST}/api/admin/sco-whitelist`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'text/csv',
Accept: 'application/json',
},
method: 'POST',
body: file,
})
.resolves(
fetchResponse({ body: { errors: [{ code: 'CERTIFICATION_INVALID_SCO_WHITELIST_ERROR' }] }, status: 422 }),
);
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByLabelText(t('pages.administration.certification.sco-whitelist.import.button'));
await triggerEvent(input, 'change', { files: [file] });

// then
assert.ok(
await screen.findByText(
t('pages.administration.certification.sco-whitelist.import.CERTIFICATION_INVALID_SCO_WHITELIST_ERROR'),
),
);
});
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { render } from '@1024pix/ember-testing-library';
import PixToastContainer from '@1024pix/pix-ui/components/pix-toast-container';
import Service from '@ember/service';
import { triggerEvent } from '@ember/test-helpers';
import { t } from 'ember-intl/test-support';
import ScoWhitelistConfiguration from 'pix-admin/components/administration/certification/sco-whitelist-configuration';
import ENV from 'pix-admin/config/environment';
import { module, test } from 'qunit';
import sinon from 'sinon';

import setupIntlRenderingTest from '../../../../helpers/setup-intl-rendering';

const accessToken = 'An access token';
const fileContent = 'foo';
const file = new Blob([fileContent], { type: `valid-file` });

module('Integration | Component | administration/certification/sco-whitelist-configuration', function (hooks) {
setupIntlRenderingTest(hooks);

let fetchStub, fileSaverStub;

hooks.beforeEach(function () {
class SessionService extends Service {
data = { authenticated: { access_token: accessToken } };
}
this.owner.register('service:session', SessionService);

class FileSaver extends Service {
save = fileSaverStub;
}

this.owner.register('service:file-saver', FileSaver);

fetchStub = sinon.stub(window, 'fetch');
fileSaverStub = sinon.stub();
});

hooks.afterEach(function () {
window.fetch.restore();
});

module('Export', function () {
module('when export succeeds', function () {
test('it succeeds', async function (assert) {
// given
fileSaverStub.resolves();
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByText(t('pages.administration.certification.sco-whitelist.export.button'));
await triggerEvent(input, 'click');

// then
assert
.dom(await screen.queryByText(t('pages.administration.certification.sco-whitelist.export.error')))
.doesNotExist();
});
});

module('when export fails', function () {
test('it displays an error notification', async function (assert) {
// given
fileSaverStub.rejects();
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByText(t('pages.administration.certification.sco-whitelist.export.button'));
await triggerEvent(input, 'click');

// then
assert.dom(await screen.getByText(t('pages.administration.certification.sco-whitelist.export.error'))).exists();
});
});
});

module('Import', function () {
module('when import succeeds', function (hooks) {
hooks.beforeEach(function () {
fetchStub
.withArgs(`${ENV.APP.API_HOST}/api/admin/sco-whitelist`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'text/csv',
Accept: 'application/json',
},
method: 'POST',
body: file,
})
.resolves(fetchResponse({ status: 201 }));
});

test('it displays a success notification', async function (assert) {
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.getByLabelText(t('pages.administration.certification.sco-whitelist.import.button'));
await triggerEvent(input, 'change', { files: [file] });

// then
assert
.dom(await screen.getByText(t('pages.administration.certification.sco-whitelist.import.success')))
.exists();
});
});

module('when import fails', function () {
test('it displays an error notification', async function (assert) {
// given
fetchStub
.withArgs(`${ENV.APP.API_HOST}/api/admin/sco-whitelist`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'text/csv',
Accept: 'application/json',
},
method: 'POST',
body: file,
})
.rejects();
// when
const screen = await render(<template><ScoWhitelistConfiguration /><PixToastContainer /></template>);
const input = await screen.findByLabelText(t('pages.administration.certification.sco-whitelist.import.button'));
await triggerEvent(input, 'change', { files: [file] });

// then
assert.dom(await screen.getByText(t('pages.administration.certification.sco-whitelist.import.error'))).exists();
});
});
});
});

function fetchResponse({ body, status }) {
const mockResponse = new window.Response(JSON.stringify(body), {
status,
headers: {
'Content-type': 'application/json',
},
});

return mockResponse;
}
12 changes: 9 additions & 3 deletions admin/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -413,11 +413,17 @@
},
"sco-whitelist": {
"title": "SCO whitelist",
"export": {
"button": "Export whitelist (CSV)",
"error": "Could not download SCO whitelist."
},
"import": {
"button": "Import new CSV file as whitelist",
"CERTIFICATION_INVALID_SCO_WHITELIST_ERROR": "An error has occurred. Please verify externalIds in csv.",
"button": "Import file as whitelist (CSV)",
"error": "Could not save SCO whitelist",
"success": "SCO whitelist saved"
}
"success": "SCO whitelist saved."
},
"instructions": "Recommended: make an export, modify the export, and import the modified version."
},
"scoring-simulator": {
"title": "Scoring simulator",
Expand Down
12 changes: 9 additions & 3 deletions admin/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -423,11 +423,17 @@
},
"sco-whitelist": {
"title": "Liste blanche centres SCO",
"export": {
"button": "Exporter la liste blanche (format CSV)",
"error": "Échec de la récupération de la liste blanche."
},
"import": {
"button": "Importer une nouvelle liste blanche au format CSV",
"CERTIFICATION_INVALID_SCO_WHITELIST_ERROR": "Une erreur est survenue. Veuillez vérifier les externalId renseignés.",
"button": "Importer une nouvelle liste blanche (format CSV)",
"error": "Échec de l'enregistrement de la liste blanche",
"success": "Liste blanche enregistrée"
}
"success": "Liste blanche enregistrée."
},
"instructions": "Recommandation: exporter la liste, modifier l'export, et envoyer la version modifiée."
},
"scoring-simulator": {
"title": "Simulateur de scoring",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HttpErrors } from '../../../shared/application/http-errors.js';
import { DomainErrorMappingConfiguration } from '../../../shared/application/models/domain-error-mapping-configuration.js';
import { InvalidScoWhitelistError } from '../domain/errors.js';

export const configurationDomainErrorMappingConfiguration = [
{
name: InvalidScoWhitelistError.name,
httpErrorFn: (error) => new HttpErrors.UnprocessableEntityError(error.message, error.code, error.meta),
},
].map((domainErrorMappingConfiguration) => new DomainErrorMappingConfiguration(domainErrorMappingConfiguration));
Loading

0 comments on commit 04a614e

Please sign in to comment.