/* * Copyright (C) 2017 Centro de Computacao Cientifica e Software Livre * Departamento de Informatica - Universidade Federal do Parana * * This file is part of blend. * * blend is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * blend is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with blend. If not, see <http://www.gnu.org/licenses/>. */ import { Client, PoolConfig } from "pg"; import { View, LoadView } from "../../src/core/view"; import { Source } from "../../src/core/source"; import { each, series } from "async"; import * as fs from "fs"; interface TransSet { init: string; data: string[]; } interface SoucerParse{ name: string; type: string[]; fields: string[]; } export interface Schema { alias?: string; query?: string; data?: string; fields: any[]; } export class Fixture { private database: string; private config: PoolConfig; constructor(config: PoolConfig) { this.config = config; this.database = config.database; } public load(schemas: LoadView[], create: boolean, cb: (err: Error) => void): void { let client = new Client(this.config); /* Loading data has 2 steps: 1 - Create a table or truncate a existing one. Create or truncate dependes from a parameter in the configure file, this parameter reaches this function as the create parameter. 2 - Insert data. */ let init: string[] = []; let data: string[] = []; for (let i = 0; i < schemas.length; ++i) { let table: TransSet = this.createTransSet(schemas[i].view, schemas[i].data, create); init.push(table.init); data = data.concat(table.data); } client.connect((error) => { if (error) { cb(error); return; } /* Tables must be creates before data could be inserted, so the queries that create and insert are splited in 2 arrays. To garantee that tables exists before start insert data, series is used. Inside series each query is executed, using each; */ series([(callback: (err: Error) => void) => { each(init, (query, cback) => { return client.query(query, [], (err: Error) => cback(err)); }, (errQuery: Error) => callback(errQuery)); }, (callback: (err: Error) => void) => { each(data, (query, cback) => { return client.query(query, [], (err: Error) => cback(err)); }, (errQuery: Error) => callback(errQuery)); }], (errQuery: Error) => { if (errQuery) { client.end(); cb(errQuery); } client.end((err) => { cb(err); }); }); }); } private typeConvertion(t: string): string { switch (t) { case "integer": return "INTEGER"; case "date": return "DATE"; case "string": return "TEXT"; case "boolean": return "BOOLEAN"; default: return "TEXT"; } } private createTransSet(view: View, filePath: string, create: boolean): TransSet { let props = []; for (let i = 0; i < view.metrics.length; ++i) { let met = view.metrics[i]; props.push("\"" + met.name + "\" " + this.typeConvertion(met.dataType)); } for (let i = 0; i < view.dimensions.length; ++i) { let dim = view.dimensions[i]; props.push("\"" + dim.name + "\" " + this.typeConvertion(dim.dataType)); } let name = "view_" + view.id; let transaction: TransSet = {init: "", data: []}; if (create) { transaction.init = "CREATE TABLE " + name + "(" + props.join(", ") + ")"; } else { transaction.init = "TRUNCATE TABLE " + name; } transaction.data = []; let rows = JSON.parse(fs.readFileSync(filePath, {encoding : "utf8"})); for (let i = 0; i < rows.length; ++i) { let values = []; let keys = []; for (let key of Object.keys(rows[i])) { keys.push("\"" + key + "\""); values.push("'" + rows[i][key] + "'"); } transaction.data.push("INSERT INTO " + name + "(" + keys.join(", ") + ") " + "VALUES (" + values.join(", ") + ")"); } return transaction; } public LoadSource(source: Source[], create: boolean , cb: (err: Error) => void): void { let client = new Client(this.config); let query: string[] = []; for (let i = 0; i < source.length; i++ ){ query[i] = this.ExtractData(source[i], create); } client.connect((error) => { if (error) { cb(error); return; } series([(callback: (err: Error) => void) => { each(query, (insere, cback) => { return client.query(insere , [], (err: Error) => cback(err)); }, (errQuery: Error) => callback(errQuery)); }], (errQuery: Error) => { if (errQuery) { client.end(); cb(errQuery); } client.end((err) => { cb(err); }); }); }); } private ExtractData(data: Source , create: boolean): string{ let name: string; let type: string[]; let fields: string[]; let consult: string; name = data.name; type = data.fields.map((item) => item.dataType); fields = data.fields.map((item) => item.name); if (create){ consult = "CREATE TABLE " + name + " (" + '"'; for (let i = 0; i < fields.length; i++){ fields[i] = fields[i].concat('"' + " " + this.typeConvertion(type[i])); } consult = consult.concat(fields.join(", " + '"')); consult = consult.concat(");"); } else{ consult = "TRUNCATE TABLE " + name + ";"; } return consult; } }