Select Git revision
configParser.ts
-
Rafael Dias authored
Signed-off-by:
Rafael <rpd17@inf.ufpr.br>
Rafael Dias authoredSigned-off-by:
Rafael <rpd17@inf.ufpr.br>
configParser.ts 14.92 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 { EnumType, EnumTypeOptions } from "../core/enumType";
import { RelationType, DataType } from "../common/types";
import { Opcode } from "../common/expression";
import { Filter } from "../core/filter";
import { Clause } from "../core/clause";
import { Source, SourceOptions, SourceStrOptions} from "../core/source";
import { Tsort, TsortDep } from "./tsort";
import * as fs from "fs";
import * as yaml from "js-yaml";
import { EnumHandler } from "./enumHandler";
/**
* Parameters used to define view object in the configuration file.
*/
export interface ViewParsingOptions {
/** Fake identifier (human understandable) of the view. */
alias: string;
/** Path to file with fixture data. */
data?: string;
/** Path to file SQL view build code. */
file?: string;
/** Inform if this view cannot be generated by other views. */
origin?: boolean;
/** Set of references (name) to dimension contained into the view. */
dimensions: string[];
/** Set of references (name) to metrics contained into the view. */
metrics: string[];
/** Set of (stringified) clauses applied to the view. */
clauses?: string[];
}
/**
* Representation of the configuration file.
*/
interface ConfigSchema {
/** Options of all sources available */
sources: SourceStrOptions[];
/** Options of all views available */
views: ViewParsingOptions[];
/** Options of all metrics available */
metrics: MetricStrOptions[];
/** Options of all dimensions available */
dimensions: DimensionStrOptions[];
/** Options of all enumerable types available */
enumTypes: EnumTypeOptions[];
}
/** Information required to build a SQL view code. */
interface BuildView {
/** View to be built. */
view: View;
/** Path to file with SQL view code. */
file: string;
/** Human understandable identifier. */
alias: string;
}
export interface ParsedConfig {
/** What adapter is in use. */
adapter: string;
/** Connection parameters to connect in the database. */
connection: Connection;
/** Set of all views available. */
views: View[];
/** Set of all sources available. */
sources: Source[];
/** Set of all metrics available. */
metrics: Metric[];
/** Set of all enumerable types available. */
enumTypes: EnumType[];
/** Set of all dimensions available. */
dimensions: Dimension[];
loadViews: LoadView[];
/** SQL views build configuration. */
buildViews: BuildView[];
}
/** Required information to connect in a generic database. */
export interface Connection {
/** Database user. */
user: string;
/** Database name. */
database: string;
/** Database password. */
password: string;
/** Database hostname. */
host: string;
/** Database port. */
port: number;
}
/** Dictonary indexed by dimension name, that returns the dimension object. */
interface DimensionMap {
[key: string]: Dimension;
}
/** Dictonary indexed by dimension name, that returns the dimension options. */
interface DimensionOptsMap {
[key: string]: DimensionStrOptions;
}
/** Dictonary indexed by metric name, that returns the metric object. */
interface MetricMap {
[key: string]: Metric;
}
/**
* Dictonary indexed by enumerable type name,
* that returns the enumerable type objetct.
*/
interface EnumTypeMap{
[key: string]: EnumType;
}
/**
* Dictonary indexed by source type name,
* that returns the source objetct.
*/
interface SourceMap {
[key: string]: Source;
}
/**
* Responsable for reading the configuration file, parse its
* string structures in internal objects understable by BlenDB.
*/
export class ConfigParser {
/**
* Parse the configuration file.
* @param configPath - Path to configuration file.
*/
public static parse(configPath: string): ParsedConfig {
let config: ConfigSchema = yaml.safeLoad(fs.readFileSync(configPath, {
encoding: "utf-8"
})) as ConfigSchema;
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)
};
let metricsOpts = config.metrics;
let viewsOpts = config.views;
let dimensionsOpts = config.dimensions;
let enumTypesOpts = config.enumTypes;
let sourcesOpts = config.sources;
let parsed: ParsedConfig = {
adapter: process.env.BLENDB_ADAPTER || "postgres",
connection: connection,
views: [],
metrics: [],
enumTypes: [],
dimensions: [],
loadViews: [],
buildViews: [],
sources: []
};
let metMap: MetricMap = {};
let dimMap: DimensionMap = {};
let enumMap: EnumTypeMap = {};
let sourcMap: SourceMap = {};
let dimOptsMap: DimensionOptsMap = {};
for (let i = 0; i < enumTypesOpts.length; i++) {
let enumT = new EnumType((enumTypesOpts[i]));
parsed.enumTypes.push(enumT);
enumMap[enumT.name] = enumT;
}
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, enumMap));
parsed.dimensions.push(dim);
dimMap[dim.name] = dim;
}
for (let i = 0; i < sourcesOpts.length; i++) {
let sourc = new Source(this.parseSourceOpts(sourcesOpts[i], enumMap));
parsed.sources.push(sourc);
sourcMap[sourc.name] = sourc;
}
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;
}
/**
* Parse a view struct in configuration file.
* @param opts - View struct in configuration file.
* @param metMap - Dictionary with available metrics.
* @param dimMap - Dictionary with available dimensions.
*/
public static parseViewOpt(opts: ViewParsingOptions,
metMap: MetricMap,
dimMap: DimensionMap): ViewOptions {
let viewOpt: ViewOptions = {
metrics: [],
dimensions: [],
origin: opts.origin,
clauses: [],
operation: {
opcode: Opcode.PUSH,
values: []
}
};
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);
}
}
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;
}
/**
* Parse a dimension struct in configuration file.
* @param opts - Dimension struct in configuration file.
* @param dims - Parsed dimensions. Parent candidates.
* @param map - Enumerable types available.
*/
public static parseDimOpts (opts: DimensionStrOptions, dims: Dimension[], map: EnumTypeMap): DimensionOptions {
let type = EnumHandler.parseDataType(opts.dataType);
if (type === DataType.NONE) {
if (!(map[opts.dataType])) {
throw new Error("[Parsing error] DataType: '" + opts.dataType + "' does not exist on Dimension");
}
}
if (opts.parent || opts.relation) {
for (let i = 0; i < dims.length; ++i) {
if (dims[i].name === opts.parent) {
return {
name: opts.name,
dataType: EnumHandler.parseDataType(opts.dataType),
description: opts.description,
parent: dims[i],
relation: EnumHandler.parseRelationType(opts.relation),
enumType: (EnumHandler.parseDataType(opts.dataType) === DataType.NONE) ? opts.dataType : ""
};
}
}
throw new Error("[Parsing error] Parent for subdimension " + opts.name + " not found");
}
return {
name: opts.name,
dataType: EnumHandler.parseDataType(opts.dataType),
description: opts.description,
parent: null,
relation: RelationType.NONE,
enumType: (EnumHandler.parseDataType(opts.dataType) === DataType.NONE) ? opts.dataType : ""
};
}
/**
* Parse a metric struct in configuration file.
* @param opts - Metric struct in configuration file.
*/
public static parseMetOpts (opts: MetricStrOptions): MetricOptions {
let type = EnumHandler.parseDataType(opts.dataType);
if (!(type === DataType.FLOAT || type === DataType.INTEGER)){
throw new Error("[Parsing error] DataType: '" + opts.dataType + "' does not exist on Metric");
}
return {
name: opts.name,
aggregation: EnumHandler.parseAggrType(opts.aggregation),
dataType : EnumHandler.parseDataType(opts.dataType),
description: opts.description
};
}
/**
* Parse a clause struct in configuration file.
* @param opts - Clause struct in configuration file.
* @param metMap - Dictionary with available metrics.
* @param dimMap - Dictionary with available dimensions.
*/
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});
}
/**
* Parse a source struct in configuration file.
* @param opts - Source struct in configuration file.
* @param map - Dictionary with available enumerable types.
*/
public static parseSourceOpts (opts: SourceStrOptions , map: EnumTypeMap): SourceOptions {
for ( let k = 0; k < opts.fields.length ; k++) {
let type = EnumHandler.parseDataType(opts.fields[k].dataType);
if (type === DataType.NONE) {
if (!(map[opts.fields[k].dataType])){
throw new Error("[Parsing error] DataType: '" + opts.fields[k].dataType + "' does not exist on Source");
}
}
}
return {
name: opts.name,
description: opts.description,
fields : Source.parseFieldDataType(opts.fields),
};
}
/**
* Parse a filter struct in configuration file.
* @param opts - Filter struct in configuration file.
* @param metMap - Dictionary with available metrics.
* @param dimMap - Dictionary with available dimensions.
*/
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
});
}
}