From 56efcb1a60936d589992131a96c06551c9e078cc Mon Sep 17 00:00:00 2001 From: Benjamin Petetot Date: Fri, 8 Nov 2024 15:56:17 +0100 Subject: [PATCH] refactor(api): migrate mass create user accounts script --- .../scripts/mass-create-user-accounts.js | 131 ++++++++---------- .../files/mass-create-user-accounts.csv | 3 + .../mass-create-user-accounts.script.test.js | 55 ++++++-- .../mass-create-user-accounts.script.test.js | 33 ----- 4 files changed, 103 insertions(+), 119 deletions(-) create mode 100644 api/tests/identity-access-management/integration/scripts/files/mass-create-user-accounts.csv rename api/tests/identity-access-management/{acceptance => integration}/scripts/mass-create-user-accounts.script.test.js (66%) delete mode 100644 api/tests/unit/scripts/identity-access-management/mass-create-user-accounts.script.test.js diff --git a/api/src/identity-access-management/scripts/mass-create-user-accounts.js b/api/src/identity-access-management/scripts/mass-create-user-accounts.js index 25a1c9e82e8..002ed4c7390 100644 --- a/api/src/identity-access-management/scripts/mass-create-user-accounts.js +++ b/api/src/identity-access-management/scripts/mass-create-user-accounts.js @@ -1,86 +1,63 @@ -/* eslint-disable no-console */ +import Joi from 'joi'; -import * as url from 'node:url'; - -import { disconnect } from '../../../db/knex-database-connection.js'; -import { parseCsvWithHeader } from '../../../scripts/helpers/csvHelpers.js'; +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 { DomainTransaction } from '../../shared/domain/DomainTransaction.js'; import { cryptoService } from '../../shared/domain/services/crypto-service.js'; import * as userService from '../../shared/domain/services/user-service.js'; import * as authenticationMethodRepository from '../infrastructure/repositories/authentication-method.repository.js'; import { userToCreateRepository } from '../infrastructure/repositories/user-to-create.repository.js'; -function prepareDataForInsert(rawUsers) { - return rawUsers.map(({ firstName, lastName, email, password }) => { - return { - firstName: firstName.trim(), - lastName: lastName.trim(), - email: email.trim().toLowerCase(), - password: password.trim(), - }; - }); -} - -async function createUsers({ usersInRaw }) { - await DomainTransaction.execute(async () => { - const now = new Date(); - - for (const userDTO of usersInRaw) { - const userToCreate = { - firstName: userDTO.firstName, - lastName: userDTO.lastName, - email: userDTO.email, - createAt: now, - updatedAt: now, - cgu: true, - lang: 'fr', - }; - const hashedPassword = await cryptoService.hashPassword(userDTO.password); - - await userService.createUserWithPassword({ - user: userToCreate, - hashedPassword, - userToCreateRepository, - authenticationMethodRepository, - }); - } - }); -} - -const modulePath = url.fileURLToPath(import.meta.url); -const isLaunchedFromCommandLine = process.argv[1] === modulePath; - -async function main() { - console.log('Starting creating users accounts for contest.'); - - const filePath = process.argv[2]; - - console.log('Reading and parsing csv data file... '); - const csvData = await parseCsvWithHeader(filePath); - console.log('ok'); - - console.log('Preparing data... '); - const usersInRaw = prepareDataForInsert(csvData); - console.log('ok'); - - console.log('Creating users...'); - await createUsers({ usersInRaw }); - console.log('\nDone.'); -} - -(async () => { - if (isLaunchedFromCommandLine) { - try { - await main(); - } catch (error) { - console.error(error); - process.exitCode = 1; - } finally { - await disconnect(); - } +export const csvSchemas = [ + { name: 'firstName', schema: Joi.string().trim().required() }, + { name: 'lastName', schema: Joi.string().trim().required() }, + { name: 'email', schema: Joi.string().trim().lowercase().email().required() }, + { name: 'password', schema: Joi.string().trim().required() }, +]; + +export class MassCreateUserAccountsScript extends Script { + constructor() { + super({ + description: 'This script allows you to create Pix user accounts with email/password', + permanent: true, + options: { + file: { + type: 'string', + describe: 'CSV file path', + demandOption: true, + coerce: csvFileParser(csvSchemas), + }, + }, + }); } -})(); - -/* eslint-enable no-console */ + async handle({ options }) { + const { file } = options; + + await DomainTransaction.execute(async () => { + const now = new Date(); + + for (const userDTO of file) { + const userToCreate = { + firstName: userDTO.firstName, + lastName: userDTO.lastName, + email: userDTO.email, + createAt: now, + updatedAt: now, + cgu: true, + lang: 'fr', + }; + const hashedPassword = await cryptoService.hashPassword(userDTO.password); + + await userService.createUserWithPassword({ + user: userToCreate, + hashedPassword, + userToCreateRepository, + authenticationMethodRepository, + }); + } + }); + } +} -export { createUsers, prepareDataForInsert }; +await ScriptRunner.execute(import.meta.url, MassCreateUserAccountsScript); diff --git a/api/tests/identity-access-management/integration/scripts/files/mass-create-user-accounts.csv b/api/tests/identity-access-management/integration/scripts/files/mass-create-user-accounts.csv new file mode 100644 index 00000000000..90e9e0a9062 --- /dev/null +++ b/api/tests/identity-access-management/integration/scripts/files/mass-create-user-accounts.csv @@ -0,0 +1,3 @@ +firstName,lastName,email,password +Dik,Tektive,dik.tektive@example.net,P@ssW0rd +Foo , Bar , foo@bar.com , barbarbar diff --git a/api/tests/identity-access-management/acceptance/scripts/mass-create-user-accounts.script.test.js b/api/tests/identity-access-management/integration/scripts/mass-create-user-accounts.script.test.js similarity index 66% rename from api/tests/identity-access-management/acceptance/scripts/mass-create-user-accounts.script.test.js rename to api/tests/identity-access-management/integration/scripts/mass-create-user-accounts.script.test.js index 01044dca3ba..1a36d56542c 100644 --- a/api/tests/identity-access-management/acceptance/scripts/mass-create-user-accounts.script.test.js +++ b/api/tests/identity-access-management/integration/scripts/mass-create-user-accounts.script.test.js @@ -1,16 +1,51 @@ -import { createUsers } from '../../../../src/identity-access-management/scripts/mass-create-user-accounts.js'; +import * as url from 'node:url'; + +import { MassCreateUserAccountsScript } from '../../../../src/identity-access-management/scripts/mass-create-user-accounts.js'; import { expect, knex, sinon } from '../../../test-helper.js'; -describe('Acceptance | Identity Access Management | Scripts | mass-create-user-accounts', function () { - describe('#createUsers', function () { +const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); + +describe('Integration | Identity Access Management | Scripts | mass-create-user-accounts', function () { + describe('Options', function () { + it('has the correct options', function () { + const script = new MassCreateUserAccountsScript(); + + const { options } = script.metaInfo; + expect(options.file).to.deep.include({ + type: 'string', + describe: 'CSV file path', + demandOption: true, + }); + }); + + it('parses CSV data correctly', async function () { + const testCsvFile = `${currentDirectory}files/mass-create-user-accounts.csv`; + + const script = new MassCreateUserAccountsScript(); + + const { options } = script.metaInfo; + const parsedData = await options.file.coerce(testCsvFile); + expect(parsedData).to.be.an('array').that.deep.includes({ + firstName: 'Dik', + lastName: 'Tektive', + email: 'dik.tektive@example.net', + password: 'P@ssW0rd', + }); + expect(parsedData).to.be.an('array').that.deep.includes({ + firstName: 'Foo', + lastName: 'Bar', + email: 'foo@bar.com', + password: 'barbarbar', + }); + }); + }); + + describe('#handle', function () { const now = new Date(); let clock; beforeEach(async function () { - clock = sinon.useFakeTimers({ - now, - toFake: ['Date'], - }); + clock = sinon.useFakeTimers({ now, toFake: ['Date'] }); }); afterEach(async function () { @@ -35,7 +70,8 @@ describe('Acceptance | Identity Access Management | Scripts | mass-create-user-a ]; // when - await createUsers({ usersInRaw }); + const script = new MassCreateUserAccountsScript(); + await script.handle({ options: { file: usersInRaw } }); // then const firstUserFound = await knex('users').where({ lastName: 'Kilo' }).first(); @@ -87,7 +123,8 @@ describe('Acceptance | Identity Access Management | Scripts | mass-create-user-a ]; // when - await createUsers({ usersInRaw }); + const script = new MassCreateUserAccountsScript(); + await script.handle({ options: { file: usersInRaw } }); // then const usersInDatabases = await knex('authentication-methods'); diff --git a/api/tests/unit/scripts/identity-access-management/mass-create-user-accounts.script.test.js b/api/tests/unit/scripts/identity-access-management/mass-create-user-accounts.script.test.js deleted file mode 100644 index 7291e3e5161..00000000000 --- a/api/tests/unit/scripts/identity-access-management/mass-create-user-accounts.script.test.js +++ /dev/null @@ -1,33 +0,0 @@ -import { prepareDataForInsert } from '../../../../src/identity-access-management/scripts/mass-create-user-accounts.js'; -import { expect } from '../../../test-helper.js'; - -describe('Unit | Identity Access Management | Scripts | mass-create-user-accounts', function () { - describe('#prepareDataForInsert', function () { - it('should trim firstName, lastName, email and password', function () { - // given - const data = [ - { - firstName: ' Salim ', - lastName: ' Bienléongle ', - email: ' salim@example.net ', - password: ' pix123 ', - }, - { firstName: ' Samantha ', lastName: ' Lo ', email: ' lo@example.net ', password: ' pixou123 ' }, - ]; - - // when - const result = prepareDataForInsert(data); - - // then - expect(result).to.deep.equal([ - { - firstName: 'Salim', - lastName: 'Bienléongle', - email: 'salim@example.net', - password: 'pix123', - }, - { firstName: 'Samantha', lastName: 'Lo', email: 'lo@example.net', password: 'pixou123' }, - ]); - }); - }); -});