ULL-ESIT-INF-DSI-2122 / ull-esit-inf-dsi-21-22-prct10-async-fs-process-alu0101321745

ull-esit-inf-dsi-21-22-prct10-async-fs-process-alu0101321745 created by GitHub Classroom
0 stars 0 forks source link

Práctica 10 - Sistema de ficheros y creación de procesos en Node.js

Badges.

Coverage Status

Tests

Coveralls

SonarClound

Algunas tareas previas.

  1. Repositorio: click aquí

  2. Api de callbacks proporcionada por Node.js: click aquí

Ejercicio 1:

Considerando el siguiente código:

import {access, constants, watch} from 'fs';

if (process.argv.length !== 3) {
  console.log('Please, specify a file');
} else {
  const filename = process.argv[2];

  access(filename, constants.F_OK, (err) => {
    if (err) {
      console.log(`File ${filename} does not exist`);
    } else {
      console.log(`Starting to watch file ${filename}`);

      const watcher = watch(process.argv[2]);

      watcher.on('change', () => {
        console.log(`File ${filename} has been modified somehow`);
      });

      console.log(`File ${filename} is no longer watched`);
    }
  });
}

Decir paso a paso el contenido de la pila de llamadas, el registro de eventos de la API, la cola de manejadores de Node.js y la salida por consola:

Ejercicio 2:

Debemos realizar un programa que devuelva el número de ocurrencias de una palabra en un fichero de texto.

Para esto vamos a desarrollar un comando yarg y 3 clases.

Comando yarg:

Recibe como parámetro el nombre del fichero con la extensión y la palabra a buscar, el código es el siguiente:

import { FilePipe } from './FilePipe';
import { FileNoPipe } from './FileNoPipe';
import * as yargs from 'yargs';
/**
 * Comando para trabajar sobre el archivo que
 * realizaremos el grep -o y wc -l con la palabra
 * indicada por consola.
 * Usage:
 * ```sh
 * node dist/Ej2/app.js file --file='hola.txt' --word="hola" --pipe=true
 * node dist/Ej2/app.js file --file='hola.txt' --word="hola" --pipe=false
 * ```
 */
yargs.command({
    command: 'file',
    describe: 'Make a pipe or not pipe callout',
    builder: {
        file: {
            describe: 'File to work',
            demandOption: true,
            type: 'string',
        },
        word: {
            describe: 'Word to count',
            demandOption: true,
            type: 'string',
        },
        pipe: {
            describe: 'Use or not pipes',
            demandOption: true,
            type: 'boolean',
        },
    },
    handler(argv) {
        if (typeof argv.file === 'string' && typeof argv.word === 'string' && typeof argv.pipe === 'boolean') {
            if (argv.pipe === true) {
                const file = new FilePipe(argv.file, argv.word);
                file.management();
            } else {
                const file = new FileNoPipe(argv.file, argv.word);
                file.management();
            }
        } else {
            console.log('Bad usage of the this.command.');
        }
    },
});

yargs.argv;

Clases:

/**
 * Clase abstracta que gestionará las peticiones sobre el fichero.
 * @param name : Nombre del fichero con la extensión.
 * @param word : Palabra a contar dentro del fichero name.
 * @method management() : Método a implementar en las clases correspondientes con y sin pipes.
 */
export abstract class FileManagement {
    constructor(protected name:string, protected word: string) { }
    getName() {return this.name;}
    getWord() {return this.word;}
    abstract management(): void;
}
import { FileManagement } from "./FileManagement";
import {spawn} from 'child_process';
import * as fs from "fs";
/**
 * Clase que usará pipes para contar las palabras dentro del fichero.
 * @param nombre : Nombre del fichero.
 * @param palabra : Palabra a contar.
 * @method management() : Metodo para contar las palabras con el uso de pipes.
 */
export class FilePipe extends FileManagement {
    constructor(protected nombre: string, protected palabra: string) {
        super(nombre, palabra);
    }
    management(): void {
        const path = `/home/usuario/Informes practicas/P10/src/Ej2/${this.getName()}`;
        if (!fs.existsSync(path)) {
            console.log("This file doesn`t exist.");
            return undefined;
        }
        const cat = spawn('cat', [path]);
        const grep = spawn('grep', ['-o', this.getWord()]);
        const wc = spawn('wc', ['-l']);

        cat.stdout.pipe(grep.stdin);
        grep.stdout.pipe(wc.stdin);

        let wcOut = '';
        wc.stdout.on('data', (data) => {
            wcOut = data.toString();
        });
        wc.stdout.on("close", () => {
            console.log('Numero de veces que se conto la palabra ' + this.getWord() + ': ' + wcOut);
        });
    }
}
import { FileManagement } from "./FileManagement";
import {spawn} from 'child_process';

/**
 * Clase que no usará pipes para contar las palabras dentro del fichero.
 * @param nombre : Nombre del fichero.
 * @param palabra : Palabra a contar.
 * @method management() : Metodo para contar las palabras sin el uso de pipes.
 */
export class FileNoPipe extends FileManagement {
    constructor(protected nombre: string, protected palabra: string) {
        super(nombre, palabra);
    }
    management(): void {
        const path = `/home/usuario/Informes practicas/P10/src/Ej2/${this.getName()}`;
        const grep = spawn('grep', ['-o', this.getWord(), path]);
        let grepOut = '';
        grep.stdout.on('data', (data) => {
            grepOut = data.toString();
        });
        grep.stdout.on("close", () => {
            console.log('Numero de veces que se conto la palabra ' + this.getWord() + `: ${grepOut.split(this.getWord()).length - 1}`);
        });
    }
}

Pruebas:

import 'mocha';
import {expect} from 'chai';
import { FileManagement } from "../src/Ej2/FileManagement";
import { FilePipe } from "../src/Ej2/FilePipe";
import { FileNoPipe } from "../src/Ej2/FileNoPipe";

describe('Pruebas del ejercicio 2:', () => {
    const filePipe = new FilePipe('hola.txt', 'hola');
    const fileNoPipe = new FileNoPipe('hola.txt', 'hola');
    it('Los ficheros son instancias de File Management:', () => {
        expect(filePipe).to.be.instanceOf(FileManagement);
        expect(fileNoPipe).to.be.instanceOf(FileManagement);
    });
    it('FilePipe es instancia de su clase:', () => {
        expect(filePipe).to.be.instanceOf(FilePipe);
    });
    it('FileNoPipe es instancia de su clase:', () => {
        expect(fileNoPipe).to.be.instanceOf(FileNoPipe);
    });
    it('Ambas clases cuentan 4 holas en el fichero de texto.:', () => {
        expect(filePipe.management()).to.be.eql(undefined);
        expect(fileNoPipe.management()).to.be.eql(undefined);
    });
});

Ejercicio 3:

Para este ejercicio desarrollamos una clase Watcher que se encargará de gestionar los eventos sobre el directorio de un usuario.

Dicho usuario será pasado por parámetro al constructor de la clase que se encargará de invocar al método para comprobar los cambios dentro del directorio del usuario, el código propuesto es el siguiente:

import * as fs from 'fs';
/**
 * Clase watcher que notificará los cambios sobre las notas de un usuario.
 * @param name : Nombre del usuario.
 * @method watchDir : Se encargará de gesitonar todo lo referente a los eventos del watcher correspondiente.
 * @method checkDir : Cada vez que se llame esta función se encargará de comprobar que existe el directorio del usuario.
 */
export class Watcher {
    constructor(private name: string) {
        this.watchDir();
    }
    watchDir() {
        let check = this.checkDir(this.name);
        if (check) {
            console.log(`Esperando por un evento`);
            const watcher = fs.watch(`/home/usuario/Informes practicas/P10/src/Ej3/usuarios/${this.name}`);
            watcher.on('change', (eventType, filename) => {
                switch (eventType) {
                    case 'rename':
                        check = this.checkDir(filename.toString());
                        if (check) {
                            console.log(`Nota ${filename} añadida.`);
                        } else {
                            console.log(`Nota ${filename} borrada.`);
                        }
                        break;
                    case 'change':
                        console.log(`Nota ${filename} modificada.`);
                        break;
                }
                console.log(`Esperando por un evento`);
            });
            return;
        } else {
            console.log('Error no existe el usuario especificado.');
            return -1;
        }
    }
    checkDir(name: string): boolean {
        try {
            fs.accessSync(`/home/usuario/Informes practicas/P10/src/Ej3/usuarios/${name}`, fs.constants.F_OK);
            return true;
        } catch {
            return false;
        }
    }
}

De este ejercicio no se crearán pruebas ya que el usuario debe interactuar con las diferentes notas.

Ejercicio 4:

Estándares a cumplir:

  1. Dada una ruta concreta, mostrar si es un directorio o un fichero.

    Para esto se usa la función fs.access() junto con las constantes constants.O_DIRECTORY. Un ejemplo de dicha funcionalidad sería la siguiente:

    function Comprobar(ruta: string): boolean | err {
    fs.access(ruta, (err) => {
    if (err) {
    console.log(`Error, no existe el directorio o archivo indicado`);
    return err;
    } else {
    fs.open(ruta, fs.constants.O_DIRECTORY, (err) => {
    if (err) {
    console.log(`El archivo es un fichero`);
    return false;
    } else {
    console.log(`\El archivo es un directorio`);
    return true;
    }
    });
    }
    });
    }
  2. Crear un > Hay dos formas de hacerlo, o usamos spawn con rm o usamos fs.unlink(path)nuevo directorio a partir de una nueva ruta que recibe como parámetro.

    Para realizar esto solo hay que usar las funcionalidades fs.access() y fs.mdkir(), un ejemplo sería el siguiente:

    function CrearDirectorio(ruta: string) {
    fs.access(ruta, (err) => {
    if (!err) {
    console.log(`Error, el directorio ya existe`);
    } else {
    fs.mkdir(ruta, (err) => {
    if (err) {
    console.log(`Error, no se ha podido crear el directorio, porfavor revise la ruta especificada`);
    } else {
    console.log(`Directorio creado`);
    }
    });
    }
    });
    }
  3. Listar los ficheros dentro de un directorio.

    Para esto solo hace falta comprobar que la ruta exista y que sea un directorio. Una vez tenemos la certeza de que la ruta y directorio existen solo hace falta el uso de spawn para hacer el comando ls.

    function listar(ruta: string) {
    if(Comprobar(ruta)) {
    const ls = spawn('ls', [ruta]);
    ls.stdout.pipe(process.stdout);
    } else {
    console.log(`La ruta especificada es incorrecta, no existe o no es un directorio`);
    }
    }
  4. Mostrar el contenido de un fichero (similar a ejecutar el comando cat).

    Para ello usaremos la función spawn para hacer el uso de cat y mediante sus manejadores simularlo por consola:

    function mostrarContenido(ruta: string) {
    fs.access(ruta, (err) => {
    if (err) {
    console.log(`No existe el fichero.`);
    } else {
    fs.open(ruta, fs.constants.O_DIRECTORY, (err) => {
    if (err) {
    const cat = spawn('cat', [ruta]);
    cat.stdout.pipe(process.stdout);
    } else {
    console.log(`La ruta indicada es un directorio, no se puede mostrar su contenido.`);
    }
    });
    }
    });
    }
  5. Borrar ficheros y directorios.

    Hay dos formas de hacerlo, o usamos spawn con rm o usamos fs.unlink(path).

  6. Mover y copiar ficheros y/o directorios de una ruta a otra. Para este caso, la aplicación recibirá una ruta origen y una ruta destino. En caso de que la ruta origen represente un directorio, se debe copiar dicho directorio y todo su contenido a la ruta destino.

    Para realizar este ejercicio haremos uso del siguiente código:

function Copy(rutaOrigen: string, rutaDestino: string) {
  fs.access(`${rutaOrigen}`, (err) => {
    if (err) {
      console.log(`No existe el fichero`);
    } else {
      fs.copyFile(rutaOrigen, rutaDestino, (err) => {
        if (err) {
          console.log(`Error copiando el archivo, revise las rutas.`);
        } else {
          console.log(`Archivo copiado satisfactoriamente.`);
        }
      });
    }
  });
}