Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Ajout d'un script pour supprimer des orga, dpo et tags associés (PIX-16091) #11124

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions api/db/seeds/data/team-acces/build-organizations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function buildOrganizations(databaseBuilder) {
_buildOrganizationWithoutAdmins(databaseBuilder);
}

function _buildOrganizationWithoutAdmins(databaseBuilder) {
const organization = databaseBuilder.factory.buildOrganization({
type: 'PRO',
name: 'Accis',
});
lego-technix marked this conversation as resolved.
Show resolved Hide resolved

const tag1 = databaseBuilder.factory.buildTag({ name: 'tag1' });
const tag2 = databaseBuilder.factory.buildTag({ name: 'tag2' });
databaseBuilder.factory.buildDataProtectionOfficer.withOrganizationId({
firstName: 'justin',
lastName: 'instant',
email: '[email protected]',
organizationId: organization.id,
});
databaseBuilder.factory.buildOrganizationTag({
organizationId: organization.id,
tagId: tag1.id,
});
databaseBuilder.factory.buildOrganizationTag({ organizationId: organization.id, tagId: tag2.id });
}
2 changes: 2 additions & 0 deletions api/db/seeds/data/team-acces/data-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { buildBlockedUsers } from './build-blocked-users.js';
import { buildCertificationCenters } from './build-certification-centers.js';
import { buildOidcProviders } from './build-oidc-providers.js';
import { buildOrganizationUsers } from './build-organization-users.js';
import { buildOrganizations } from './build-organizations.js';
import { buildPixAdminRoles } from './build-pix-admin-roles.js';
import { buildResetPasswordUsers } from './build-reset-password-users.js';
import { buildScoOrganizationLearners } from './build-sco-organization-learners.js';
Expand All @@ -20,6 +21,7 @@ async function teamAccesDataBuilder(databaseBuilder) {
buildScoOrganizationLearners(databaseBuilder);
await buildCertificationCenters(databaseBuilder);
await buildOidcProviders(databaseBuilder);
await buildOrganizations(databaseBuilder);
}

export { teamAccesDataBuilder };
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ async function create(dataProtectionOfficer) {
return new DataProtectionOfficer(dataProtectionOfficerRow);
}

async function deleteDpoByOrganizationId(organizationId) {
const knexConn = DomainTransaction.getConnection();
await knexConn(DATA_PROTECTION_OFFICERS_TABLE_NAME).where({ organizationId }).delete();
}

async function update(dataProtectionOfficer) {
const knexConn = DomainTransaction.getConnection();
const { firstName, lastName, email, organizationId, certificationCenterId } = dataProtectionOfficer;
Expand All @@ -54,4 +59,4 @@ async function update(dataProtectionOfficer) {
return new DataProtectionOfficer(dataProtectionOfficerRow);
}

export { batchAddDataProtectionOfficerToOrganization, create, get, update };
export { batchAddDataProtectionOfficerToOrganization, create, deleteDpoByOrganizationId, get, update };
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,14 @@ const update = async function (organization) {
};

/**
* @typedef {Object} OrganizationForAdminRepository
* @property {archive} archive
* @property {exist} exist
* @property {findChildrenByParentOrganizationId} findChildrenByParentOrganizationId
* @property {get} get
* @property {save} save
* @property {update} update
* @type {function}
* @param organizationId
* @return {Promise<void>}
*/
const deleteById = async function (organizationId) {
const knexConn = DomainTransaction.getConnection();
await knexConn(ORGANIZATIONS_TABLE_NAME).where({ id: organizationId }).delete();
};

async function _addOrUpdateDataProtectionOfficer(knexConn, dataProtectionOfficer) {
await knexConn(DATA_PROTECTION_OFFICERS_TABLE_NAME)
Expand Down Expand Up @@ -320,4 +320,12 @@ function _toDomain(rawOrganization) {
return organization;
}

export const organizationForAdminRepository = { archive, exist, findChildrenByParentOrganizationId, get, save, update };
export const organizationForAdminRepository = {
archive,
exist,
findChildrenByParentOrganizationId,
get,
save,
update,
deleteById,
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ const getRecentlyUsedTags = async function ({ tagId, numberOfRecentTags }) {
return tags.map(({ tagId: id, name }) => new Tag({ id, name }));
};

export { batchCreate, create, getRecentlyUsedTags, isExistingByOrganizationIdAndTagId };
const deleteTagsByOrganizationId = async function (organizationId) {
const knexConn = DomainTransaction.getConnection();
await knexConn('organization-tags').where({ organizationId }).delete();
};

export { batchCreate, create, deleteTagsByOrganizationId, getRecentlyUsedTags, isExistingByOrganizationIdAndTagId };
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Joi from 'joi';

import { csvFileParser } from '../../shared/application/scripts/parsers.js';
import { Script } from '../../shared/application/scripts/script.js';
import { ScriptRunner } from '../../shared/application/scripts/script-runner.js';
import * as dataProtectionOfficerRepository from '../infrastructure/repositories/data-protection-officer.repository.js';
import { organizationForAdminRepository } from '../infrastructure/repositories/organization-for-admin.repository.js';
import * as organizationTagRepository from '../infrastructure/repositories/organization-tag.repository.js';

const columnsSchema = [{ name: 'Organization ID', schema: Joi.number().required() }];

export class DeleteOrganizationsScript extends Script {
constructor() {
super({
description: 'Delete all organizations and associated tags',
permanent: false,
options: {
file: {
type: 'string',
describe: 'File path to CSV file with organizations to delete',
demandOption: true,
requiresArg: true,
coerce: csvFileParser(columnsSchema),
},
dryRun: {
type: 'boolean',
describe: 'Run the script without actually deleting anything',
default: false,
},
},
});
}

async handle({
options,
logger,
dependencies = { organizationForAdminRepository, organizationTagRepository, dataProtectionOfficerRepository },
}) {
const { file, dryRun } = options;

let count = 0;
for (const row of file) {
const organizationId = row['Organization ID'];
if (!dryRun) {
logger.info(organizationId);
// Delete data protection officer via data-protection-officer.repository
await dependencies.dataProtectionOfficerRepository.deleteDpoByOrganizationId(organizationId);
// delete organization tags via organization-tags.repository
await dependencies.organizationTagRepository.deleteTagsByOrganizationId(organizationId);
// delete organizationvia organization-for-admin.repository
await dependencies.organizationForAdminRepository.deleteById(organizationId);
}
count++;
}

if (dryRun) {
logger.info(`Would delete ${count} organizations.`);
} else {
logger.info(`Deleted ${count} organizations.`);
}
}
}

await ScriptRunner.execute(import.meta.url, DeleteOrganizationsScript);
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,37 @@ describe('Integration | Organizational Entities | Repository | data-protection-o
});
});

describe('#deleteByOrganizationId', function () {
it('deletes DPO from data protection officers table by organisation id ', async function () {
// given
const organization = databaseBuilder.factory.buildOrganization();
const organization2 = databaseBuilder.factory.buildOrganization();
databaseBuilder.factory.buildDataProtectionOfficer.withOrganizationId({
firstName: 'Justin',
lastName: 'Ninstan',
email: '[email protected]',
organizationId: organization.id,
});
databaseBuilder.factory.buildDataProtectionOfficer.withOrganizationId({
firstName: 'Justin',
lastName: 'Ninstan',
email: '[email protected]',
organizationId: organization2.id,
});

await databaseBuilder.commit();

// when

await dataProtectionOfficerRepository.deleteDpoByOrganizationId(organization2.id);

// then
const dataProtectionOfficers = await knex('data-protection-officers').select();
expect(dataProtectionOfficers).to.have.lengthOf(1);
expect(dataProtectionOfficers[0]).to.have.property('organizationId', organization.id);
});
});

describe('#get', function () {
context('when DPO exists', function () {
it('returns a DPO domain object', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -869,4 +869,22 @@ describe('Integration | Organizational Entities | Infrastructure | Repository |
expect(nbOrganizationsAfterUpdate).to.equal(nbOrganizationsBeforeUpdate);
});
});

describe('#deleteById', function () {
it('deletes organization by id', async function () {
// given
const organization = databaseBuilder.factory.buildOrganization();
const organization2 = databaseBuilder.factory.buildOrganization();
await databaseBuilder.commit();

// when
await organizationForAdminRepository.deleteById(organization.id);

// then
const organizationDeleted = await knex('organizations').where({ id: organization.id });
expect(organizationDeleted).to.have.lengthOf(0);
const organizationNotDeleted = await knex('organizations').where({ id: organization2.id });
expect(organizationNotDeleted).to.have.lengthOf(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,34 @@ describe('Integration | Organizational Entities | Infrastructure | Repository |
expect(foundOrganizations).to.have.lengthOf(2);
});
});

describe('#deleteTag', function () {
it('delete organizationTags', async function () {
// given
const organization1 = databaseBuilder.factory.buildOrganization();
const organization2 = databaseBuilder.factory.buildOrganization();
const tag1 = databaseBuilder.factory.buildTag({ name: 'tag1' });
const tag2 = databaseBuilder.factory.buildTag({ name: 'tag2' });
databaseBuilder.factory.buildOrganizationTag({
organizationId: organization1.id,
tagId: tag1.id,
});
databaseBuilder.factory.buildOrganizationTag({
organizationId: organization2.id,
tagId: tag2.id,
});
databaseBuilder.factory.buildOrganizationTag({
organizationId: organization1.id,
tagId: tag2.id,
});
await databaseBuilder.commit();
// when
await organizationTagRepository.deleteTagsByOrganizationId(organization1.id);
// then
const organizationTags = await knex('organization-tags').select();
expect(organizationTags).to.have.lengthOf(1);
expect(organizationTags[0]).to.have.property('tagId', tag2.id);
expect(organizationTags[0]).to.have.property('organizationId', organization2.id);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { DeleteOrganizationsScript } from '../../../../src/organizational-entities/scripts/./delete-organizations-script.js';
import { expect, sinon } from '../../../test-helper.js';

describe('DeleteOrganizationsScript', function () {
describe('Handle', function () {
let script;
let logger;
let organizationForAdminRepository;
let organizationTagRepository;
let dataProtectionOfficerRepository;

beforeEach(function () {
script = new DeleteOrganizationsScript();
logger = { info: sinon.spy() };
organizationForAdminRepository = { deleteById: sinon.stub() };
organizationTagRepository = { deleteTagsByOrganizationId: sinon.stub() };
dataProtectionOfficerRepository = { deleteDpoByOrganizationId: sinon.stub() };
});

it('handles data correctly', async function () {
const file = [{ 'Organization ID': 1 }, { 'Organization ID': 2 }];

await script.handle({
options: { file },
logger,
dependencies: {
organizationForAdminRepository,
organizationTagRepository,
dataProtectionOfficerRepository,
},
});
expect(organizationForAdminRepository.deleteById.calledWith(1)).to.be.true;
expect(organizationForAdminRepository.deleteById.calledWith(2)).to.be.true;
expect(organizationTagRepository.deleteTagsByOrganizationId.calledWith(1)).to.be.true;
expect(organizationTagRepository.deleteTagsByOrganizationId.calledWith(2)).to.be.true;
expect(dataProtectionOfficerRepository.deleteDpoByOrganizationId.calledWith(1)).to.be.true;
expect(dataProtectionOfficerRepository.deleteDpoByOrganizationId.calledWith(2)).to.be.true;
});
});
});
Loading