<commit_before>import {Db, PluginConfig, BaseApp, Log, JovoError, ErrorCode} from 'jovo-core';
import * as path from 'path';
import * as fs from "fs";
import _get = require('lodash.get');
import _set = require('lodash.set');
import _merge = require('lodash.merge');

interface Config extends PluginConfig {
    pathToFile: string;
    primaryKeyColumn: string;
}

export class FileDb implements Db {
    needsWriteFileAccess = true;

    config: Config = {
        pathToFile: './../db/db.json',
        primaryKeyColumn: 'userId',
    };

    constructor(config?: Config) {
        if (config) {
            this.config = _merge(this.config, config);
        }
    }

    install(app: BaseApp) {
        const pathToFile: string = this.config.pathToFile;
        FileDb.validatePathToFile(this.config);

        _set(this, 'config.pathToFile', pathToFile);
        // create file
        try {
            if (!fs.existsSync(path.dirname(pathToFile))) {
                FileDb.mkDirByPathSync(path.dirname(pathToFile), false);
            }
            if (!fs.existsSync(pathToFile)) {
                fs.writeFileSync(pathToFile, '[]');
                Log.info(Log.header('INFO: Local FileDb', 'db-filedb'));

                Log.info(`${path.resolve(pathToFile)} created!`);
                Log.info();

                Log.info('More Info: >> https://www.jovo.tech/docs/databases/file-db');

                Log.info(Log.header());

            }

            app.$db = this;
        } catch (e) {

        }

    }

    uninstall(app: BaseApp) {

    }
    /**
     * Returns object for given primaryKey
     * @param {string} primaryKey
     * @return {Promise<any>}
     */
    async load(primaryKey: string) {
        if (!fs.existsSync(this.config.pathToFile)) {
            throw new JovoError(
                `File db ${this.config.pathToFile} does not exist.`,
                ErrorCode.ERR_PLUGIN,
                'jovo-db-filedb',
                undefined,
                `Restart the Jovo app. ${this.config.pathToFile} will be created automatically.`);
        }
        Log.verbose(`Loading data from: ${this.config.pathToFile}`);
        const data: any = await this.readFile(this.config.pathToFile); // tslint:disable-line
        const users = data.length > 0 ? JSON.parse(data) : [];
        const userData = users.find((o:any) => { // tslint:disable-line
            return o[this.config.primaryKeyColumn] === primaryKey;
        });

        return Promise.resolve(userData);
    }

    async save(primaryKey: string, key: string, data: any) { // tslint:disable-line
        if (!fs.existsSync(this.config.pathToFile)) {
            throw new JovoError(
                `File db ${this.config.pathToFile} does not exist.`,
                ErrorCode.ERR_PLUGIN,
                'jovo-db-filedb',
                undefined,
                `Restart the Jovo app. ${this.config.pathToFile} will be created automatically.`);
        }

        const oldData: any = await this.readFile(this.config.pathToFile); // tslint:disable-line
        const users = oldData.length > 0 ? JSON.parse(oldData) : [];

        // find data for user with this primaryKey
        const userData = users.find((o:any) => { // tslint:disable-line
            return o[this.config.primaryKeyColumn] === primaryKey;
        });

        if(userData) {
            _set(userData, key, data);
        } else {
            const newData: any = {}; // tslint:disable-line
            newData[this.config.primaryKeyColumn] = primaryKey;
            _set(newData, key, data);
            users.push(newData);
        }
        Log.verbose(`Saving data to: ${this.config.pathToFile}`);

        return this.saveFile(this.config.pathToFile, users);
    }

    async delete(primaryKey: string) {
        if (!fs.existsSync(this.config.pathToFile)) {
            throw new JovoError(
                `File db ${this.config.pathToFile} does not exist.`,
                ErrorCode.ERR_PLUGIN,
                'jovo-db-filedb',
                undefined,
                `Restart the Jovo app. ${this.config.pathToFile} will be created automatically.`);
        }

        const data: any = await this.readFile(this.config.pathToFile); // tslint:disable-line
        let users = data.length > 0 ? JSON.parse(data) : [];
        let rowsAffected = 0;
        for (let i = 0; i < users.length; i++) {
            if (users[i][this.config.primaryKeyColumn] === primaryKey) {
                delete users[i];
                rowsAffected++;
            }
        }
        users = users.filter((n:object) => n); // remove null

        await this.saveFile(this.config.pathToFile, users);
        return Promise.resolve(rowsAffected);
    }


    private async readFile(filename: string) {
        return new Promise<any>((resolve, reject) => { // tslint:disable-line
            fs.readFile(filename, 'utf8', (err, data) => {
                if (err) {
                    return reject(err);
                }
                resolve(data);
            });<commit_msg>:recycle: Refactor error handling and config types<commit_after>import {Db, PluginConfig, BaseApp, Log, JovoError, ErrorCode} from 'jovo-core';
import * as path from 'path';
import * as fs from "fs";
import _get = require('lodash.get');
import _set = require('lodash.set');
import _merge = require('lodash.merge');

interface Config extends PluginConfig {
    pathToFile?: string;
    primaryKeyColumn?: string;
}

export class FileDb implements Db {
    needsWriteFileAccess = true;

    config: Config = {
        pathToFile: './../db/db.json',
        primaryKeyColumn: 'userId',
    };

    constructor(config?: Config) {
        if (config) {
            this.config = _merge(this.config, config);
        }
    }

    install(app: BaseApp) {
        const pathToFile = this.config.pathToFile;
        FileDb.validatePathToFile(this.config);

        _set(this, 'config.pathToFile', pathToFile);
        // create file
        try {
            if (!fs.existsSync(path.dirname(pathToFile!))) {
                FileDb.mkDirByPathSync(path.dirname(pathToFile!), false);
            }
            if (!fs.existsSync(pathToFile!)) {
                fs.writeFileSync(pathToFile!, '[]');
                Log.info(Log.header('INFO: Local FileDb', 'db-filedb'));

                Log.info(`${path.resolve(pathToFile!)} created!`);
                Log.info();

                Log.info('More Info: >> https://www.jovo.tech/docs/databases/file-db');

                Log.info(Log.header());

            }

            app.$db = this;
        } catch (e) {

        }

    }

    uninstall(app: BaseApp) {

    }

    errorHandling() {
        if (!fs.existsSync(this.config.pathToFile!)) {
            throw new JovoError(
                `File db ${this.config.pathToFile} does not exist.`,
                ErrorCode.ERR_PLUGIN,
                'jovo-db-filedb',
                undefined,
                `Restart the Jovo app. ${this.config.pathToFile} will be created automatically.`
            );
        }
    }

    /**
     * Returns object for given primaryKey
     * @param {string} primaryKey
     * @return {Promise<any>}
     */
    async load(primaryKey: string) {
        this.errorHandling();

        Log.verbose(`Loading data from: ${this.config.pathToFile}`);
        const data: any = await this.readFile(this.config.pathToFile!); // tslint:disable-line
        const users = data.length > 0 ? JSON.parse(data) : [];
        const userData = users.find((o:any) => { // tslint:disable-line
            return o[this.config.primaryKeyColumn!] === primaryKey;
        });

        return Promise.resolve(userData);
    }

    async save(primaryKey: string, key: string, data: any) { // tslint:disable-line
        this.errorHandling();

        const oldData: any = await this.readFile(this.config.pathToFile!); // tslint:disable-line
        const users = oldData.length > 0 ? JSON.parse(oldData) : [];

        // find data for user with this primaryKey
        const userData = users.find((o:any) => { // tslint:disable-line
            return o[this.config.primaryKeyColumn!] === primaryKey;
        });

        if(userData) {
            _set(userData, key, data);
        } else {
            const newData: any = {}; // tslint:disable-line
            newData[this.config.primaryKeyColumn!] = primaryKey;
            _set(newData, key, data);
            users.push(newData);
        }
        Log.verbose(`Saving data to: ${this.config.pathToFile}`);

        return await this.saveFile(this.config.pathToFile!, users);
    }

    async delete(primaryKey: string) {
        this.errorHandling();

        const data: any = await this.readFile(this.config.pathToFile!); // tslint:disable-line
        let users = data.length > 0 ? JSON.parse(data) : [];
        let rowsAffected = 0;
        for (let i = 0; i < users.length; i++) {
            if (users[i][this.config.primaryKeyColumn!] === primaryKey) {
                delete users[i];
                rowsAffected++;
            }
        }
        users = users.filter((n:object) => n); // remove null

        await this.saveFile(this.config.pathToFile!, users);
        return Promise.resolve(rowsAffected);
    }


    private async readFile(filename: string) {
        return new Promise<any>((resolve, reject) => { // tslint:disable-line
            fs.readFile(filename, 'utf8', (err, data) => {
                if (err) {
                    return reject(err);
                }
                resolve(data);
            });
{'fraction_non_alphanumeric': 0.1340443329150983, 'fraction_numerical': 0.0012547051442910915, 'mean_word_length': 1.9576376004947433, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6807210', 'n_tokens_mistral': 2890, 'n_tokens_neox': 2767, 'n_words': 588}