<commit_before>// This module retrieves real values from the weboob backend, by using the given
// bankuuid / login / password (maybe customFields) combination.
import { spawn } from 'child_process';
import * as path from 'path';

import { makeLogger, KError } from '../../helpers';

let log = makeLogger('sources/weboob');

export let SOURCE_NAME = 'weboob';

function callWeboob(command, access) {
    return new Promise((accept, reject) => {
        log.info(`Calling weboob: command ${command}...`);

        let serverRoot = path.join(__filename, '..', '..', '..');
        let script = spawn(`./weboob/main.py`, [], { cwd: serverRoot });

        script.stdin.write(`${command}\n`);

        if (command === 'accounts' || command === 'transactions') {
            let { bank: bankuuid, login, password, customFields } = access;
            script.stdin.write(`${bankuuid}\n`);
            script.stdin.write(`${login}\n`);
            script.stdin.write(`${password}\n`);
            if (typeof customFields !== 'undefined')
                script.stdin.write(`${customFields}\n`);
        }

        script.stdin.end();

        let body = '';
        script.stdout.on('data', data => {
            body += data.toString();
        });

        let err;
        script.stderr.on('data', data => {
            err = err || '';
            err += data.toString();
        });

        script.on('close', code => {

            log.info(`exited with code ${code}`);

            if (err && err.trim().length) {
                log.info(`stderr: ${err}`);
            }

            if (code !== 0) {
                log.info('Command left with non-zero code.');
                reject(new KError(`Weboob failure: ${err}`));
                return;
            }

            if (command === 'test' || command === 'update') {
                accept();
                return;
            }

            try {
                body = JSON.parse(body);
            } catch (e) {
                reject(new KError(`Error when parsing weboob json:
- stdout: ${body}
- stderr: ${err}
- JSON error: ${e}`));
                return;
            }

            if (typeof body.error_code !== 'undefined') {
                log.warn(`Weboob error, stderr: ${err}`);
                let error = new KError(`Weboob error: ${error.content}`,
                                       500, body.error_code);
                reject(error);
                return;
            }

            log.info('OK: weboob exited normally with non-empty JSON content.');
            accept(body.values);
        });
    });
}

export async function testInstall() {
    try {
        await callWeboob('test');
        return true;
    } catch (err) {
        log.error(`When testing install: ${err}`);
        return false;
    }
}

async function testInstallAndFetch(command, access) {
    if (await testInstall())
        return await callWeboob(command, access);
    throw new KError("Weboob doesn't seem to be installed, skipping fetch.");
}

export async function fetchAccounts(access) {
    return await testInstallAndFetch('accounts', access);
}
<commit_msg>Make Weboob error reporting simpler;<commit_after>// This module retrieves real values from the weboob backend, by using the given
// bankuuid / login / password (maybe customFields) combination.
import { spawn } from 'child_process';
import * as path from 'path';

import { makeLogger, KError } from '../../helpers';

let log = makeLogger('sources/weboob');

export let SOURCE_NAME = 'weboob';

function callWeboob(command, access) {
    return new Promise((accept, reject) => {
        log.info(`Calling weboob: command ${command}...`);

        let serverRoot = path.join(__filename, '..', '..', '..');
        let script = spawn(`./weboob/main.py`, [], { cwd: serverRoot });

        script.stdin.write(`${command}\n`);

        if (command === 'accounts' || command === 'transactions') {
            let { bank: bankuuid, login, password, customFields } = access;
            script.stdin.write(`${bankuuid}\n`);
            script.stdin.write(`${login}\n`);
            script.stdin.write(`${password}\n`);
            if (typeof customFields !== 'undefined')
                script.stdin.write(`${customFields}\n`);
        }

        script.stdin.end();

        let stdout = '';
        script.stdout.on('data', data => {
            stdout += data.toString();
        });

        let stderr;
        script.stderr.on('data', data => {
            stderr = stderr || '';
            stderr += data.toString();
        });

        script.on('close', code => {

            log.info(`exited with code ${code}`);

            if (stderr && stderr.trim().length) {
                log.info(`stderr: ${stderr}`);
            }

            if (code !== 0) {
                log.info('Command left with non-zero code.');
                reject(new KError(`Weboob failure: ${stderr}`));
                return;
            }

            if (command === 'test' || command === 'update') {
                accept();
                return;
            }

            let parseJsonError = null;
            try {
                stdout = JSON.parse(stdout);
            } catch (e) {
                parseJsonError = e.stack;
            }

            if (parseJsonError || typeof stdout.error_code !== 'undefined') {
                log.warn(`Weboob error, stderr: ${stderr}`);
                let error = new KError(`Error when parsing weboob json:
- stdout: ${stdout}
- stderr: ${stderr}
- JSON error: ${parseJsonError},
- error_code: ${stdout.error_code}`, 500, stdout.error_code);
                reject(error);
                return;
            }

            log.info('OK: weboob exited normally with non-empty JSON content.');
            accept(stdout.values);
        });
    });
}

export async function testInstall() {
    try {
        await callWeboob('test');
        return true;
    } catch (err) {
        log.error(`When testing install: ${err}`);
        return false;
    }
}

async function testInstallAndFetch(command, access) {
    if (await testInstall())
        return await callWeboob(command, access);
    throw new KError("Weboob doesn't seem to be installed, skipping fetch.");
}

export async function fetchAccounts(access) {
    return await testInstallAndFetch('accounts', access);
}

{'fraction_non_alphanumeric': 0.15125786163522012, 'fraction_numerical': 0.0012578616352201257, 'mean_word_length': 1.8926784902228286, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6060274', 'n_tokens_mistral': 1791, 'n_tokens_neox': 1749, 'n_words': 445}