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

configParser.ts

Blame
  • Forked from C3SL / blendb
    113 commits behind the upstream repository.
    configParser.ts 9.15 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 } from "../core/metric";
    import { Dimension, DimensionOptions } from "../core/dimension";
    import { View, ViewOptions, LoadView } from "../core/view";
    import { AggregationType, RelationType } from "../common/types";
    import { Filter } from "../core/filter";
    import { Clause } from "../core/clause";
    import { PoolConfig } from "pg";
    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 MetricStrOptions {
        name: string;
        aggregation: string;
        dataType: string;
    }
    
    export interface DimensionStrOptions {
        name: string;
        dataType: string;
        parent?: string;
        relation?: string;
    }
    
    interface ConfigSchema {
        views: ViewParsingOptions[];
        metrics: MetricStrOptions[];
        dimensions: DimensionStrOptions[];
    }
    
    interface ConfigFile {
        connection: PoolConfig;
        struct: LoadStruct;
        schema: ConfigSchema;
    }
    
    interface BuildView {
        view: View;
        file: string;
        alias: string;
    }
    
    export interface LoadStruct{
        create: boolean;
        insert: boolean;
    }
    
    export interface ParsedConfig {
        connection: PoolConfig;
        views: View[];
        metrics: Metric[];
        dimensions: Dimension[];
        struct: LoadStruct;
        loadViews: LoadView[];
        buildViews: BuildView[];
    }
    
    interface DimensionMap {
        [key: string]: Dimension;
    }
    
    interface MetricMap {
        [key: string]: Metric;
    }
    
    export class ConfigParser {
        public static parse(configPath: string): ParsedConfig {
            let config: ConfigFile = yaml.safeLoad(fs.readFileSync(configPath, {
                encoding: "utf-8"
            }));
    
            let metricsOpts = config.schema.metrics;
            let viewsOpts = config.schema.views;
            let dimensionsOpts = config.schema.dimensions;
            let parsed: ParsedConfig = {
                connection: config.connection,
                views: [],
                metrics: [],
                dimensions: [],
                struct: config.struct,
                loadViews: [],
                buildViews: []
            };
    
            let metMap: MetricMap = {};
            let dimMap: DimensionMap = {};
    
            for (let i = 0; i < metricsOpts.length; ++i) {
                let met = new Metric(this.parseMetOpts(metricsOpts[i]));
                parsed.metrics.push(met);
                metMap[met.name] = met;
            }
    
            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,
                            parent: dims[i],
                            relation: this.strToRelationType(opts.relation)
                        };
                    }
                }
    
                throw new Error("[Parsing error] Parent for subdimension " + opts.name + "  not found");
            }
            return {
                name: opts.name,
                dataType: opts.dataType,
                parent: null,
                relation: RelationType.NONE
            };
        }
    
        private static parseMetOpts (opts: MetricStrOptions): MetricOptions {
            return {
                name: opts.name,
                aggregation: this.strToAggregationType(opts.aggregation),
                dataType : opts.dataType
            };
        }
    
        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
            });
        }
    
        private static strToAggregationType (str: string): AggregationType {
            switch (str) {
                case "sum":
                    return AggregationType.SUM;
                case "avg":
                    return AggregationType.AVG;
                case "count":
                    return AggregationType.COUNT;
                case "min":
                    return AggregationType.MIN;
                case "max":
                    return AggregationType.MAX;
                default:
                    return AggregationType.NONE;
            }
        }
    
        private static strToRelationType (str: string): RelationType {
            switch (str) {
                case "day":
                    return RelationType.DAY;
                case "month":
                    return RelationType.MONTH;
                case "year":
                    return RelationType.YEAR;
                case "dayofweek":
                    return RelationType.DAYOFWEEK;
                default:
                    return RelationType.NONE;
            }
        }
    }