Skip to content
Snippets Groups Projects
Select Git revision
  • develop default protected
  • simmc-based
  • drill-experiment
  • tg-felipe
  • issue/97
  • issue/63
  • icde-2019-experiments
  • issue/85
  • master protected
  • issue/20
  • refactor/engine
  • issue/6
  • feature/diagrams
  • wip-transformers
14 results

configParser.ts

Blame
  • configParser.ts 9.66 KiB
    /*
     * Copyright (C) 2017 Centro de Computacao Cientifica e Software Livre
     * Departamento de Informatica - Universidade Federal do Parana
     *
     * This file is part of blendb.
     *
     * blendb 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.
     *
     * blendb 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 blendb.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    import { Metric, MetricOptions, MetricStrOptions } from "../core/metric";
    import { Dimension, DimensionOptions, DimensionStrOptions } from "../core/dimension";
    import { View, ViewOptions, LoadView } from "../core/view";
    import { RelationType } from "../common/types";
    import { Filter } from "../core/filter";
    import { Clause } from "../core/clause";
    import { Tsort, TsortDep } from "./tsort";
    import * as fs from "fs";
    import * as yaml from "js-yaml";
    
    export interface ViewParsingOptions {
        alias: string;
        data: string;
        file?: string;
        origin?: boolean;
        dimensions: string[];
        metrics: string[];
        clauses?: string[];
        keys?: string[];
    }
    
    interface ConfigSchema {
        views: ViewParsingOptions[];
        metrics: MetricStrOptions[];
        dimensions: DimensionStrOptions[];
    }
    
    interface BuildView {
        view: View;
        file: string;
        alias: string;
    }
    
    export interface LoadStruct{
        create: boolean;
        insert: boolean;
    }
    
    export interface ParsedConfig {
        adapter: string;
        connection: Connection;
        views: View[];
        metrics: Metric[];
        dimensions: Dimension[];
        struct: LoadStruct;
        loadViews: LoadView[];
        buildViews: BuildView[];
    }
    
    export interface Connection {
        user: string;
        database: string;
        password: string;
        host: string;
        port: number;
    }
    
    interface DimensionMap {
        [key: string]: Dimension;
    }
    
    interface DimensionOptsMap {
        [key: string]: DimensionStrOptions;
    }
    
    interface MetricMap {
        [key: string]: Metric;
    }
    
    export class ConfigParser {
        public static parse(configPath: string): ParsedConfig {
            let config: ConfigSchema = yaml.safeLoad(fs.readFileSync(configPath, {
                encoding: "utf-8"
            }));
    
            let connection: Connection = {
                user: process.env.BLENDB_DB_USER,
                database: process.env.BLENDB_DB_NAME,
                password: process.env.BLENDB_DB_PASSWORD,
                host: process.env.BLENDB_DB_HOST,
                port: parseInt(process.env.BLENDB_DB_PORT, 10)
            };
    
            const strCreate = process.env.BLENDB_ST_CREATE;
            const strInsert = process.env.BLENDB_ST_INSERT;
    
            let struct: LoadStruct = {
                create: (strCreate) ? strCreate.toLowerCase() === "true" : false,
                insert: (strInsert) ? strInsert.toLowerCase() === "true" : false
            };
    
            let metricsOpts = config.metrics;
            let viewsOpts = config.views;
            let dimensionsOpts = config.dimensions;
            let parsed: ParsedConfig = {
                adapter: process.env.BLENDB_ADAPTER || "postgres",
                connection: connection,
                views: [],
                metrics: [],
                dimensions: [],
                struct: struct,
                loadViews: [],
                buildViews: []
            };
    
            let metMap: MetricMap = {};
            let dimMap: DimensionMap = {};
            let dimOptsMap: DimensionOptsMap = {};
    
            for (let i = 0; i < metricsOpts.length; ++i) {
                let met = new Metric(this.parseMetOpts(metricsOpts[i]));
                parsed.metrics.push(met);
                metMap[met.name] = met;
            }
    
            let toSort: TsortDep[] = [];
            for (let i = 0; i < dimensionsOpts.length; ++i) {
                if (dimensionsOpts[i].parent) {
                    toSort.push({
                        value: dimensionsOpts[i].name,
                        dependOf: dimensionsOpts[i].parent
                    });
                }
    
                else {
                    toSort.push({
                        value: dimensionsOpts[i].name
                    });
                }
    
                dimOptsMap[dimensionsOpts[i].name] = dimensionsOpts[i];
            }
    
            dimensionsOpts = Tsort.dependencies(toSort).filter((name) => {
                return (dimOptsMap[name]) ? true : false;
            }).map((name) => {
                return dimOptsMap[name];
            });
    
            for (let i = 0; i < dimensionsOpts.length; ++i) {
                let dim = new Dimension(this.parseDimOpts(dimensionsOpts[i], parsed.dimensions));
                parsed.dimensions.push(dim);
                dimMap[dim.name] = dim;
            }
    
            for (let i = 0; i < viewsOpts.length; ++i) {
                if (!viewsOpts[i].metrics) {
                    viewsOpts[i].metrics = [];
                }
                let viewOpts = ConfigParser.parseViewOpt(viewsOpts[i], metMap, dimMap);
                let view = new View(viewOpts);
                parsed.views.push(view);
                let loadView: LoadView = {view: view, data: viewsOpts[i].data};
                parsed.loadViews.push(loadView);
                const buildView: BuildView = {
                    view: view,
                    file: viewsOpts[i].file,
                    alias: viewsOpts[i].alias
                };
                parsed.buildViews.push(buildView);
            }
    
            return parsed;
        }
    
        public static parseViewOpt(opts: ViewParsingOptions,
                                   metMap: MetricMap,
                                   dimMap: DimensionMap): ViewOptions {
    
            let viewOpt: ViewOptions = {
                metrics: [],
                dimensions: [],
                materialized: true,
                origin: opts.origin,
                childViews: [],
                clauses: [],
                keys: []
            };
    
            const keys = (opts.keys) ? opts.keys : [];
    
            for (let i = 0; i < opts.metrics.length; ++i) {
                if (metMap[opts.metrics[i]]) {
                    viewOpt.metrics.push(metMap[opts.metrics[i]]);
                }
    
                else {
                    throw new Error("[Parsing error] Non exist metric set to view " + opts.alias);
                }
            }
    
            for (let i = 0; i < opts.dimensions.length; ++i) {
                if (dimMap[opts.dimensions[i]]) {
                    viewOpt.dimensions.push(dimMap[opts.dimensions[i]]);
                }
    
                else {
                    throw new Error("[Parsing error] Non exist dimension set to view " + opts.alias);
                }
            }
    
            for (let i = 0; i < keys.length; ++i) {
                if (dimMap[keys[i]]) {
                    viewOpt.keys.push(dimMap[opts.keys[i]]);
                }
    
                else {
                    throw new Error("[Parsing error] Non exist key set to view " + opts.alias);
                }
            }
    
            if (opts.clauses) {
                for (let i = 0; i < opts.clauses.length; ++i) {
                    const clause = this.parseClause(opts.clauses[i], metMap, dimMap);
                    if (clause) {
                        viewOpt.clauses.push(clause);
                    }
                    else {
                        throw new Error("[Parsing error] Bad formed clause: on view \"" +
                                        opts.alias + "\" the clause \"" + opts.clauses[i] +
                                        "\" could not be created");
                    }
                }
            }
            return viewOpt;
        }
    
        public static parseDimOpts (opts: DimensionStrOptions, dims: Dimension[]): DimensionOptions {
            if (opts.parent || opts.relation) {
                for (let i = 0; i < dims.length; ++i) {
                    if (dims[i].name === opts.parent) {
                        return {
                            name: opts.name,
                            dataType: opts.dataType,
                            description: opts.description,
                            parent: dims[i],
                            relation: Dimension.parseRelationType(opts.relation)
                        };
                    }
                }
    
                throw new Error("[Parsing error] Parent for subdimension " + opts.name + "  not found");
            }
            return {
                name: opts.name,
                dataType: opts.dataType,
                description: opts.description,
                parent: null,
                relation: RelationType.NONE
            };
        }
    
        private static parseMetOpts (opts: MetricStrOptions): MetricOptions {
            return {
                name: opts.name,
                aggregation: Metric.parseAggrType(opts.aggregation),
                dataType : opts.dataType,
                description: opts.description
            };
        }
    
        private static parseClause (opts: string, metMap: MetricMap, dimMap: DimensionMap): Clause {
            const strFilters = opts.split(",");
            const filters: Filter[] = strFilters.map((item) => {
                return this.parseFilter(item, metMap, dimMap);
            });
    
            for (let i = 0; i < filters.length; ++i) {
                if (!filters[i] || !filters[i].isValid) {
                    return null;
                }
            }
    
            return new Clause ({filters: filters});
        }
    
        private static parseFilter (opts: string, metMap: MetricMap, dimMap: DimensionMap): Filter {
            const strFilter = Filter.segment(opts);
            if (!strFilter) {
                return null;
            }
            let target: Metric|Dimension = dimMap[strFilter.target];
            if (!target) {
                target = metMap[strFilter.target];
                if (!target) {
                    return null;
                }
            }
    
            const operator = Filter.parseOperator(strFilter.operator);
    
            return new Filter({
                target: target,
                operator: operator,
                value: strFilter.value
            });
        }
    
    }