Skip to content

Commit

Permalink
✨ Ajout d'une commande cli pour vérifier l'existance de fichiers S3
Browse files Browse the repository at this point in the history
  • Loading branch information
benjlevesque committed Oct 9, 2024
1 parent 4fcb985 commit 11a045a
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 14 deletions.
74 changes: 74 additions & 0 deletions packages/applications/cli/src/commands/files/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Command, Flags } from '@oclif/core';
import { z } from 'zod';

import { fileExists } from '@potentiel-libraries/file-storage';

import { parseCsvFile, csvFlags } from '../../helpers/parse-file';
import { makeReporter, reporterFlags } from '../../helpers/reporter';

const schema = z.object({
from: z.string(),
to: z.string().optional(),
});

export default class FilesRename extends Command {
static override description = 'Check existance of a list of files from a CSV';
static override examples: Command.Example[] = [
{
command: "npx cli files check --path ./file.csv --delimiter ','",
description: 'change the delimiter to ,',
},
{
command: "npx cli files check --path ./file.csv --delimiter 'utf8'",
description: 'change the encoding to utf8',
},
{
command: 'npx cli files check --path ./file.csv --report out.csv --reportErrorsOnly',
description: 'reports errors to out.csv',
},
];

static override args = {};

static override flags = {
path: Flags.file({
exists: true,
char: 'p',
description: 'path to the csv file containing the files to check',
required: true,
}),
field: Flags.string({
description: 'name of the csv field containing the file to check',
default: 'from',
}),
...csvFlags,
...reporterFlags,
};

public async run(): Promise<void> {
const { flags } = await this.parse(FilesRename);
const { parsedData: files } = await parseCsvFile(flags.path, schema, {
delimiter: flags.delimiter,
encoding: flags.encoding,
});

const reporter = await makeReporter(flags);

for (const file of files) {
const field = flags.field as keyof z.infer<typeof schema>;
const filename = file[field];
if (!filename) {
console.info(`Missing filename`);
continue;
}
console.info(`Checking ${filename}`);
const exists = await fileExists(filename);
if (exists) {
await reporter.success(filename);
} else {
await reporter.error(filename);
}
}
console.info(`Checked ${files.length} files`);
}
}
19 changes: 13 additions & 6 deletions packages/applications/cli/src/commands/files/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { z } from 'zod';

import { copyFile } from '@potentiel-libraries/file-storage';

import { parseCsvFile } from '../../helpers/parse-file';
import { csvFlags, parseCsvFile } from '../../helpers/parse-file';
import { makeReporter, reporterFlags } from '../../helpers/reporter';

const schema = z.object({
from: z.string(),
Expand All @@ -22,23 +23,29 @@ export default class FilesCopy extends Command {
description: 'path to the csv file containing the files to copy',
required: true,
}),
...csvFlags,
...reporterFlags,
};

public async run(): Promise<void> {
const { flags } = await this.parse(FilesCopy);
const { parsedData: files } = await parseCsvFile(flags.path, schema);
const { parsedData: files } = await parseCsvFile(flags.path, schema, {
delimiter: flags.delimiter,
encoding: flags.encoding,
});
const reporter = await makeReporter(flags);

for (const file of files) {
try {
console.info(`Copying ${file.from} to ${file.to}`);
await copyFile(file.from, file.to);
console.debug(`Copied ${file.from} to ${file.to}`);
await reporter.success(file.from);
} catch (e) {
console.log(e);

console.error(`Error while copying ${file.from}`, e);
await reporter.error(file.to, e);
}
}
await reporter.close();

console.info(`Completed ${files.length} files`);
}
}
23 changes: 15 additions & 8 deletions packages/applications/cli/src/commands/files/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { z } from 'zod';

import { renameFile } from '@potentiel-libraries/file-storage';

import { parseCsvFile } from '../../helpers/parse-file';
import { parseCsvFile, csvFlags } from '../../helpers/parse-file';
import { makeReporter, reporterFlags } from '../../helpers/reporter';

const schema = z.object({
from: z.string(),
Expand All @@ -17,28 +18,34 @@ export default class FilesRename extends Command {
static override args = {};

static override flags = {
path: Flags.string({
path: Flags.file({
exists: true,
char: 'p',
description: 'path to the csv file containing the files to rename',
description: 'path to the existing csv file containing the files to rename',
required: true,
}),
...csvFlags,
...reporterFlags,
};

public async run(): Promise<void> {
const { flags } = await this.parse(FilesRename);
const { parsedData: files } = await parseCsvFile(flags.path, schema);
const { parsedData: files } = await parseCsvFile(flags.path, schema, {
delimiter: flags.delimiter,
encoding: flags.encoding,
});
const reporter = await makeReporter(flags);

for (const file of files) {
try {
console.info(`Renaming ${file.from} to ${file.to}`);
await renameFile(file.from, file.to);
console.debug(`Renamed ${file.from} to ${file.to}`);
await reporter.success(file.from);
} catch (e) {
console.log(e);

console.error(`Error while moving ${file.from}`, e);
await reporter.error(file.from, e);
}
}
await reporter.close();
console.info(`Completed ${files.length} files`);
}
}
12 changes: 12 additions & 0 deletions packages/applications/cli/src/helpers/parse-file.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import { readFile } from 'node:fs/promises';

import { z } from 'zod';
import { Flags } from '@oclif/core';

import { CsvValidationError, parseCsv, ParseOptions } from '@potentiel-libraries/csv';

export const csvFlags = {
delimiter: Flags.string({
options: [',', ';'] as const,
default: ';',
}),
encoding: Flags.option({
options: ['utf8', 'win1252'] as const,
default: 'win1252' as const,
})(),
};

export const parseCsvFile = async <T extends z.ZodRawShape>(
path: string,
schema: z.ZodObject<T>,
Expand Down
88 changes: 88 additions & 0 deletions packages/applications/cli/src/helpers/reporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import fs from 'fs/promises';

import { Flags } from '@oclif/core';

export const reporterFlags = {
report: Flags.file({
description: 'the name of the file to report to',
}),
reportErrorsOnly: Flags.boolean({
description: `only outputs to the csv file`,
dependsOn: ['report'],
}),
};

type Reporter = {
success: (obj: string | Record<string, unknown>) => void | Promise<void>;
error: (obj: string | Record<string, unknown>, error?: unknown) => void | Promise<void>;
close: () => void | Promise<void>;
};

const consoleReporter: Reporter = {
success: (obj) => {
console.log('✅', obj);
},
error: (obj, error) => {
console.log('❌', obj, error);
},
close: () => {},
};

const makeFileReporter = async (filePath: string) => {
// create or overwrite file
await fs.writeFile(filePath, '');
const resultsFile = await fs.open(filePath, 'a');
return {
report: (content: string) => resultsFile.appendFile(content + '\n'),
close: () => resultsFile.close(),
};
};

const makeCsvReporter = async (filePath: string, reportErrorsOnly: boolean): Promise<Reporter> => {
const fileReporter = await makeFileReporter(filePath);
return {
success: async (value) => {
if (reportErrorsOnly) {
return;
}
await fileReporter.report(['success', value, ''].join(','));
},
error: async (value, error) => {
await fileReporter.report(
['error', value, (error as Error)?.message].filter(Boolean).join(','),
);
},
close: fileReporter.close,
};
};

export const makeReporter = async ({
report,
reportErrorsOnly,
}: {
report: string | undefined;
reportErrorsOnly: boolean;
}): Promise<Reporter> => {
const reporters = [consoleReporter];
if (report) {
reporters.push(await makeCsvReporter(report, reportErrorsOnly));
}

return {
success: async (obj) => {
for (const reporter of reporters) {
await reporter.success(obj);
}
},
error: async (obj, error) => {
for (const reporter of reporters) {
await reporter.error(obj, error);
}
},
close: async () => {
for (const reporter of reporters) {
await reporter.close();
}
},
};
};

0 comments on commit 11a045a

Please sign in to comment.