Issue #84: Add code docs in Typedoc format

Signed-off-by: Lucas Fernandes de Oliveira's avatarLucas Fernandes de Oliveira <lfoliveira@inf.ufpr.br>
parent 4339c078
Pipeline #15845 passed with stages
in 1 minute and 50 seconds
...@@ -20,3 +20,4 @@ ...@@ -20,3 +20,4 @@
/database/views /database/views
/service /service
schema.sql schema.sql
/doc/code
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
"show-coverage": "xdg-open coverage/lcov-report/index.html", "show-coverage": "xdg-open coverage/lcov-report/index.html",
"doc-api": "raml2html -i specs/blendb-api-v1.raml -o doc/api-v1-reference.html", "doc-api": "raml2html -i specs/blendb-api-v1.raml -o doc/api-v1-reference.html",
"schema": "env $(cat config/config.env ) ts-node scripts/schema.ts config/config.yaml schema.sql", "schema": "env $(cat config/config.env ) ts-node scripts/schema.ts config/config.yaml schema.sql",
"service": "./scripts/service.sh" "service": "./scripts/service.sh",
"doc-code": "typedoc --mode 'file' --module 'commonjs' --target 'ES6' --ignoreCompilerErrors --exclude '**/*.spec.ts' --out 'doc/code' 'src'"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
"raml2html": "^3.0.1", "raml2html": "^3.0.1",
"supertest": "^3.0.0", "supertest": "^3.0.0",
"tslint": "^3.15.1", "tslint": "^3.15.1",
"tslint-stylish": "^2.1.0-beta" "tslint-stylish": "^2.1.0-beta",
"typedoc": "^0.11.1"
} }
} }
...@@ -23,33 +23,73 @@ import { View } from "../core/view"; ...@@ -23,33 +23,73 @@ import { View } from "../core/view";
import { Source } from "../core/source"; import { Source } from "../core/source";
import { FilterOperator } from "../core/filter"; import { FilterOperator } from "../core/filter";
import { DataType } from "../common/types"; import { DataType } from "../common/types";
/** @hidden */
const MDB = require("monetdb")(); const MDB = require("monetdb")();
/**
* Params required to connect with a MonetDB database and
* to create a MonetAdapter object.
*/
export interface MonetConfig { export interface MonetConfig {
/** Database hostname */
host: string; host: string;
/** Database port */
port: number; port: number;
/** Database name */
dbname: string; dbname: string;
/** Database user */
user: string; user: string;
/** Datavase password */
password: string; password: string;
} }
/**
* Represent the data format returned by MonetDB.
* This interface is used to parse this format to the BlenDB Standart.
*/
interface MonetResult { interface MonetResult {
/** Query result as a list of values */
data: any[]; data: any[];
/** Meta data of each attribute required. */
structure: {type: string, column: string, index: number}[]; structure: {type: string, column: string, index: number}[];
} }
/**
* Adapter which connects with a MonetDB database.
*/
export class MonetAdapter extends SQLAdapter { export class MonetAdapter extends SQLAdapter {
/** Information used to connect with a MonetDB database. */
private config: MonetConfig; private config: MonetConfig;
/**
* Creates a new adapter with the database connection configuration.
* @param conf - The information required to create a connection with
* the database.
*/
constructor (conf: MonetConfig) { constructor (conf: MonetConfig) {
super(); super();
this.config = conf; this.config = conf;
} }
/**
* Asynchronously reads all data from given view.
* In other words perform a SELECT query.
* @param view - "Location" from all data should be read.
* @param cb - Callback function which contains the data read.
* @param cb.error - Error information when the method fails.
* @param cb.result - Data got from view.
*/
public getDataFromView(view: View, cb: (error: Error, result?: any[]) => void): void { public getDataFromView(view: View, cb: (error: Error, result?: any[]) => void): void {
const query = this.getQueryFromView(view); const query = this.getQueryFromView(view);
this.executeQuery(query, cb); this.executeQuery(query, cb);
} }
/**
* Asynchronously executes a query and get its result.
* @param query - Query (SQL format) to be executed.
* @param cb - Callback function which contains the data read.
* @param cb.error - Error information when the method fails.
* @param cb.result - Query result.
*/
private executeQuery(query: string, cb: (error: Error, result?: any[]) => void): void { private executeQuery(query: string, cb: (error: Error, result?: any[]) => void): void {
let pool: any = new MDB(this.config); let pool: any = new MDB(this.config);
pool.connect(); pool.connect();
...@@ -79,15 +119,31 @@ export class MonetAdapter extends SQLAdapter { ...@@ -79,15 +119,31 @@ export class MonetAdapter extends SQLAdapter {
pool.close(); pool.close();
} }
/**
* Materialize a given view.
* @param view - View to be materialized.
*/
public materializeView(view: View): boolean { public materializeView(view: View): boolean {
return false; return false;
} }
/**
* Asynchronously insert one register into a given Source.
* @param source - Insertion "location".
* @param cb - Callback function which contains the query result.
* @param cb.error - Error information when the method fails.
* @param cb.result - Query result.
*/
public insertIntoSource(source: Source, data: any[], cb: (err: Error, result: any[]) => void): void { public insertIntoSource(source: Source, data: any[], cb: (err: Error, result: any[]) => void): void {
const query = this.getQueryFromSource(source, data); const query = this.getQueryFromSource(source, data);
this.executeQuery(query, cb); this.executeQuery(query, cb);
} }
/**
* Cast BlenDB data types to be used in MonetDB queries.
* @param quotedValue - SQL query attribute wrapped by quotes.
* @param dt - Attribute data type.
*/
protected typeCast(quotedValue: string, dt: DataType): string { protected typeCast(quotedValue: string, dt: DataType): string {
switch (dt) { switch (dt) {
case DataType.DATE: case DataType.DATE:
...@@ -101,6 +157,12 @@ export class MonetAdapter extends SQLAdapter { ...@@ -101,6 +157,12 @@ export class MonetAdapter extends SQLAdapter {
} }
} }
/**
* Translate filter operator to be used in MonetDB queries.
* @param lSide - Operation left side operator.
* @param rSide - Operation right side operator.
* @param op - Operation to be performed.
*/
protected applyOperator(lSide: string, rSide: string, op: FilterOperator): string { protected applyOperator(lSide: string, rSide: string, op: FilterOperator): string {
switch (op) { switch (op) {
case FilterOperator.EQUAL: case FilterOperator.EQUAL:
......
...@@ -25,18 +25,41 @@ import { FilterOperator } from "../core/filter"; ...@@ -25,18 +25,41 @@ import { FilterOperator } from "../core/filter";
import { Pool, PoolConfig } from "pg"; import { Pool, PoolConfig } from "pg";
import { DataType } from "../common/types"; import { DataType } from "../common/types";
/** Adapter which connects with a PostgreSQL database. */
export class PostgresAdapter extends SQLAdapter { export class PostgresAdapter extends SQLAdapter {
/** Information used to connect with a PostgreSQL database. */
private pool: Pool; private pool: Pool;
/**
* Creates a new adapter with the database connection configuration.
* @param config - The information required to create a connection with
* the database.
*/
constructor (config: PoolConfig) { constructor (config: PoolConfig) {
super(); super();
this.pool = new Pool(config); this.pool = new Pool(config);
} }
/**
* Asynchronously reads all data from given view.
* In other words perform a SELECT query.
* @param view - "Location" from all data should be read.
* @param cb - Callback function which contains the data read.
* @param cb.error - Error information when the method fails.
* @param cb.result - Data got from view.
*/
public getDataFromView(view: View, cb: (error: Error, result?: any[]) => void): void { public getDataFromView(view: View, cb: (error: Error, result?: any[]) => void): void {
const query = this.getQueryFromView(view); const query = this.getQueryFromView(view);
this.executeQuery(query, cb); this.executeQuery(query, cb);
} }
/**
* Asynchronously executes a query and get its result.
* @param query - Query (SQL format) to be executed.
* @param cb - Callback function which contains the data read.
* @param cb.error - Error information when the method fails.
* @param cb.result - Query result.
*/
private executeQuery(query: string, cb: (err: Error, result?: any[]) => void): void{ private executeQuery(query: string, cb: (err: Error, result?: any[]) => void): void{
this.pool.connect((err, client, done) => { this.pool.connect((err, client, done) => {
if (err) { if (err) {
...@@ -50,15 +73,32 @@ export class PostgresAdapter extends SQLAdapter { ...@@ -50,15 +73,32 @@ export class PostgresAdapter extends SQLAdapter {
}); });
}); });
} }
/**
* Asynchronously insert one register into a given Source.
* @param source - Insertion "location".
* @param cb - Callback function which contains the query result.
* @param cb.error - Error information when the method fails.
* @param cb.result - Query result.
*/
public insertIntoSource(source: Source, data: any[], cb: (err: Error, result?: any[]) => void): void { public insertIntoSource(source: Source, data: any[], cb: (err: Error, result?: any[]) => void): void {
const query = this.getQueryFromSource(source, data); const query = this.getQueryFromSource(source, data);
this.executeQuery(query, cb); this.executeQuery(query, cb);
} }
/**
* Materialize a given view.
* @param view - View to be materialized.
*/
public materializeView(view: View): boolean { public materializeView(view: View): boolean {
return false; return false;
} }
/**
* Cast BlenDB data types to be used in PostgreSQL queries.
* @param quotedValue - SQL query attribute wrapped by quotes.
* @param dt - Attribute data type.
*/
protected typeCast(quotedValue: string, dt: DataType): string { protected typeCast(quotedValue: string, dt: DataType): string {
switch (dt) { switch (dt) {
case DataType.DATE: case DataType.DATE:
...@@ -72,6 +112,12 @@ export class PostgresAdapter extends SQLAdapter { ...@@ -72,6 +112,12 @@ export class PostgresAdapter extends SQLAdapter {
} }
} }
/**
* Translate filter operator to be used in PostgreSQL queries.
* @param lSide - Operation left side operator.
* @param rSide - Operation right side operator.
* @param op - Operation to be performed.
*/
protected applyOperator(lSide: string, rSide: string, op: FilterOperator): string { protected applyOperator(lSide: string, rSide: string, op: FilterOperator): string {
switch (op) { switch (op) {
case FilterOperator.EQUAL: case FilterOperator.EQUAL:
......
...@@ -28,29 +28,67 @@ import { AggregationType, RelationType, DataType } from "../common/types"; ...@@ -28,29 +28,67 @@ import { AggregationType, RelationType, DataType } from "../common/types";
import { Operation, Opcode } from "../common/expression"; import { Operation, Opcode } from "../common/expression";
import { View } from "../core/view"; import { View } from "../core/view";
/**
* Information required to make a join clause.
* The dimension and views that contain this dimension.
*/
interface DimInfo { interface DimInfo {
/** Dimension object. */
dim: Dimension; dim: Dimension;
/** Set of views that contain the dimension. */
views: View[]; views: View[];
} }
/**
* Several translations of a dimension that will be used in different
* parts of a query
*/
interface DimTranslation { interface DimTranslation {
/** Translation with the AS clause. Used on SELECT clause. */
aliased: string; aliased: string;
/** Translation without the AS clause. USED on GROUP BY clause. */
noalias: string; noalias: string;
/** The alias of the dimension. The quoted name. */
alias: string; alias: string;
/** Expansion of a sub dimension. */
expanded: boolean; expanded: boolean;
} }
/** Translated view, the query which gets all data and its name */
interface QueryAndName { interface QueryAndName {
/** Query that returns all the data of a view. */
query: string; query: string;
/** The view name. */
name: string; name: string;
} }
/**
* Two Dictionaries, both indexed with a dimension name.
* Used to get the views where a dimension is.
* The dimensions also have the Dimension object inside.
*/
interface DimAndNameMap { interface DimAndNameMap {
/** Dictionary indexed by dimension name that returns DimInfo */
dimensions: {[key: string]: DimInfo}; dimensions: {[key: string]: DimInfo};
/**
* Dictionary indexed by dimension name that returns
* a view where this dimension is. Used as right side
* of a join clause.
*/
views: {[key: string]: View}; views: {[key: string]: View};
} }
/**
* Generic SQL adapter.
* Contains most implementation to perform a SQL query.
* However, which database has its peculiarities, so this
* adapter must be extended to each SGBD.
*/
export abstract class SQLAdapter extends Adapter { export abstract class SQLAdapter extends Adapter {
/**
* Translate a view to a SQL query.
* @param view - View to be translated.
*/
public getQueryFromView(view: View): string { public getQueryFromView(view: View): string {
const partials = this.buildPartials(view).filter((i) => { const partials = this.buildPartials(view).filter((i) => {
return i.query !== ""; return i.query !== "";
...@@ -76,6 +114,13 @@ export abstract class SQLAdapter extends Adapter { ...@@ -76,6 +114,13 @@ export abstract class SQLAdapter extends Adapter {
return withClause + "SELECT * FROM " + this.viewName(view) + sort + ";"; return withClause + "SELECT * FROM " + this.viewName(view) + sort + ";";
} }
/**
* Translates a view to a SQL sub-query. The main diffence between
* this method and getQueryFromView is that a sub-query cannot have
* sorting properties. Also a sub-query is called a partial and
* partials can only have one metric.
* @param view - View which the partial will be built.
*/
private buildPartials(view: View): QueryAndName[] { private buildPartials(view: View): QueryAndName[] {
let op = view.operation; let op = view.operation;
let queue: View[] = op.values.map((i) => i); let queue: View[] = op.values.map((i) => i);
...@@ -104,10 +149,19 @@ export abstract class SQLAdapter extends Adapter { ...@@ -104,10 +149,19 @@ export abstract class SQLAdapter extends Adapter {
return output; return output;
} }
/**
* The translated name of a view into SQL databases.
* @param view - View which the name will be built.
*/
protected viewName(view: View): string { protected viewName(view: View): string {
return "view_" + view.id; return "view_" + view.id;
} }
/**
* Constructs a query from a view based in a given operation
* @param op - Operation used to construct the view.
* @param view - View to be querified.
*/
private operation(op: Operation, view: View): string { private operation(op: Operation, view: View): string {
switch (op.opcode) { switch (op.opcode) {
case Opcode.REDUCE: case Opcode.REDUCE:
...@@ -121,6 +175,12 @@ export abstract class SQLAdapter extends Adapter { ...@@ -121,6 +175,12 @@ export abstract class SQLAdapter extends Adapter {
} }
} }
/**
* Constructs a query to a expecific view, from a set of given partials.
* @param view - View to be built.
* @param partials - Partials (other views) required to built the view.
* @param isJoin - Used to check if join clauses must be added.
*/
private buildOperation(view: View, partials: View[], isJoin: boolean): string { private buildOperation(view: View, partials: View[], isJoin: boolean): string {
// Mapping, which views the metrics and dimensions are // Mapping, which views the metrics and dimensions are
const mapping = this.buildMaps(partials); const mapping = this.buildMaps(partials);
...@@ -183,6 +243,11 @@ export abstract class SQLAdapter extends Adapter { ...@@ -183,6 +243,11 @@ export abstract class SQLAdapter extends Adapter {
return projection + sources + selection + grouping; return projection + sources + selection + grouping;
} }
/**
* Constructs and returns the dictionaries which inform
* in which views a given dimension is.
* @param views - Set of views to be scored.
*/
private buildMaps(views: View[]): DimAndNameMap { private buildMaps(views: View[]): DimAndNameMap {
let dimMap: {[key: string]: DimInfo} = {}; let dimMap: {[key: string]: DimInfo} = {};
let nameMap: {[key: string]: View} = {}; let nameMap: {[key: string]: View} = {};
...@@ -218,6 +283,13 @@ export abstract class SQLAdapter extends Adapter { ...@@ -218,6 +283,13 @@ export abstract class SQLAdapter extends Adapter {
}; };
} }
/**
* Returns a set o clauses of the given view that is not fulfilled
* by any of the partials. In other words, clauses that has not been
* added to the query yet.
* @param view - View with all clauses.
* @param partials - Views with some of the clauses.
*/
private orphanClauses(view: View, partials: View[]): Clause[] { private orphanClauses(view: View, partials: View[]): Clause[] {
let parentClauses: Clause[] = []; let parentClauses: Clause[] = [];
for (let i = 0; i < partials.length; ++i) { for (let i = 0; i < partials.length; ++i) {
...@@ -228,6 +300,12 @@ export abstract class SQLAdapter extends Adapter { ...@@ -228,6 +300,12 @@ export abstract class SQLAdapter extends Adapter {
return view.clauses.filter((i) => !parentClauses.some((j) => j.id === i.id)); return view.clauses.filter((i) => !parentClauses.some((j) => j.id === i.id));
} }
/**
* Parse a BlenDB enumeration of agregation functions to a proper string
* to be used in the SQL query.
* @param aggrType - Aggregation function.
* @param origin - Will be applied in a view with origin flag ?
*/
private getAggregateFunction(aggrType: AggregationType, origin: boolean): string { private getAggregateFunction(aggrType: AggregationType, origin: boolean): string {
switch (aggrType) { switch (aggrType) {
case AggregationType.SUM: case AggregationType.SUM:
...@@ -246,6 +324,12 @@ export abstract class SQLAdapter extends Adapter { ...@@ -246,6 +324,12 @@ export abstract class SQLAdapter extends Adapter {
} }
/**
* Parse a BlenDB enumeration of relationships to a proper string
* to be used in the SQL query.
* @param relation - The realtion function that will be used.
* @param arg - The attribute that is the function argument.
*/
private translateRelation(relation: RelationType, arg: string): string { private translateRelation(relation: RelationType, arg: string): string {
switch (relation) { switch (relation) {
case RelationType.DAY: case RelationType.DAY:
...@@ -260,6 +344,12 @@ export abstract class SQLAdapter extends Adapter { ...@@ -260,6 +344,12 @@ export abstract class SQLAdapter extends Adapter {
} }
/**
* Generic translate a SQL function to a query
* @param name - Function name.
* @param args - Function argument names.
* @param values - Function argument values.
*/
private applyRelation(name: string, args: string[], values: string[]): string { private applyRelation(name: string, args: string[], values: string[]): string {
/* /*
This adapter uses the concept of functions in Postgres to This adapter uses the concept of functions in Postgres to
...@@ -278,11 +368,22 @@ export abstract class SQLAdapter extends Adapter { ...@@ -278,11 +368,22 @@ export abstract class SQLAdapter extends Adapter {
return name + "(" + args.map((item, idx) => item + values[idx]).join(",") + ")"; return name + "(" + args.map((item, idx) => item + values[idx]).join(",") + ")";
} }
/**
* Add quotes and the proper view name to a attribute in a SQL query.
* @param item - Attribute to get the name quoted.
* @param id - View id used to build the view name.
*/
private buildColumn (item: Metric|Dimension, id: string): string { private buildColumn (item: Metric|Dimension, id: string): string {
const quotedName = "\"" + item.name + "\""; const quotedName = "\"" + item.name + "\"";
return "view_" + id + "." + quotedName; return "view_" + id + "." + quotedName;
} }
/**
* Translate Clause object into a SQL query condition.
* @param clause - Clause to be translated.
* @param map - Dictionary indexed bu attributes name
* containing its locations.
*/
private translateClause(clause: Clause, map: {[key: string]: View}): string { private translateClause(clause: Clause, map: {[key: string]: View}): string {
const r = clause.filters.map((item) => { const r = clause.filters.map((item) => {
return this.translateFilter(item, map); return this.translateFilter(item, map);
...@@ -292,6 +393,12 @@ export abstract class SQLAdapter extends Adapter { ...@@ -292,6 +393,12 @@ export abstract class SQLAdapter extends Adapter {
return r.join(" OR "); return r.join(" OR ");
} }
/**
* Translate Filter object into a SQL query condition.
* @param filter - Filter to be translated.
* @param map - Dictionary indexed bu attributes name
* containing its locations.
*/
private translateFilter(filter: Filter, map: {[key: string]: View}): string { private translateFilter(filter: Filter, map: {[key: string]: View}): string {
if (!map[filter.target.name]) { if (!map[filter.target.name]) {
return ""; return "";
...@@ -304,6 +411,11 @@ export abstract class SQLAdapter extends Adapter { ...@@ -304,6 +411,11 @@ export abstract class SQLAdapter extends Adapter {
return this.applyOperator(leftSide, castedValue, filter.operator); return this.applyOperator(leftSide, castedValue, filter.operator);
} }
/**
* Translate Metric object into a SQL query attribute.
* @param metric - Metric to be translated.
* @param view - View which contains the metric. Used to create the name
*/
private translateMetric(metric: Metric, view: View): string { private translateMetric(metric: Metric, view: View): string {
const func = this.getAggregateFunction(metric.aggregation, view.origin); const func = this.getAggregateFunction(metric.aggregation, view.origin);
const quotedName = "\"" + metric.name + "\""; const quotedName = "\"" + metric.name + "\"";
...@@ -311,6 +423,13 @@ export abstract class SQLAdapter extends Adapter { ...@@ -311,6 +423,13 @@ export abstract class SQLAdapter extends Adapter {
return extMetric + " AS " + quotedName; return extMetric + " AS " + quotedName;
} }
/**
* Translate Dimension object into a SQL query attribute.
* @param dimension - Dimension to be translated.
* @param ancestor - Dimension ancestor found in the view
* @param view - View which contains the dimenion/ancestor.
* Used to create the name.
*/
private translateDimension(dimension: Dimension, private translateDimension(dimension: Dimension,
ancestor: Dimension, ancestor: Dimension,
view: View): DimTranslation { view: View): DimTranslation {
...@@ -331,6 +450,11 @@ export abstract class SQLAdapter extends Adapter { ...@@ -331,6 +450,11 @@ export abstract class SQLAdapter extends Adapter {
}; };
} }
/**
* Generates a generic insertion query for one register.
* @param source - Data insertion "location".
* @param data - Data to be inserted.
*/
public getQueryFromSource(source: Source, data: any[]): string { public getQueryFromSource(source: Source, data: any[]): string {
let consult: string; let consult: string;
let colums: any[] = []; let colums: any[] = [];
...@@ -349,7 +473,17 @@ export abstract class SQLAdapter extends Adapter { ...@@ -349,7 +473,17 @@ export abstract class SQLAdapter extends Adapter {
return consult; return consult;
} }
/**
* Parse a filter operation. Varies in each database.
* @param lSide - Operation left side operator.
* @param rSide - Operation right side operator.
* @param op - Operation to be performed.
*/
protected abstract applyOperator(leftSide: string, rightSide: string, op: FilterOperator): string; protected abstract applyOperator(leftSide: string, rightSide: string, op: FilterOperator): string;
/**
* Cast BlenDB types to proper database types.
* @param quotedValue - SQL query attribute wrapped by quotes.
* @param dt - Attribute data type.
*/
protected abstract typeCast(quotedValue: string, dt: DataType): string; protected abstract typeCast(quotedValue: string, dt: DataType): string;
} }
...@@ -24,11 +24,28 @@ import { Source, Field } from "../../core/source"; ...@@ -24,11 +24,28 @@ import { Source, Field } from "../../core/source";
import { EnumType } from "../../core/enumType"; import { EnumType } from "../../core/enumType";
import { DataType } from "../../common/types"; import { DataType } from "../../common/types";
/**
* Dictionary indexed by a type name which return a
* validation function that returns true if the
* objetct is a valid object of that type
* or false otherwise.
*/
interface Valid{ interface Valid{
[key: string]: (value: any) => boolean; [key: string]: (value: any) => boolean;
} }
/**
* Constroller responsable for collect part from the API. In other
* words, controller responsable for inserting data in BlenDB.
*/
export class CollectCtrl { export class CollectCtrl {
/**
* Route that validates and insert data.
* @param req - Object with request information
* @param res - Object used to create and send the response
* @param next - Call next middleware or controller. Not used but required
* by typescript definition of route.
*/
public static write(req: Request, res: express.Response, next: express.NextFunction) { public static write(req: Request, res: express.Response, next: express.NextFunction) {
const validador: Valid = { const validador: Valid = {
......
...@@ -22,7 +22,18 @@ import * as express from "express"; ...@@ -22,7 +22,18 @@ import * as express from "express";
import { Request } from "../types"; import { Request } from "../types";
import { Query } from "../../common/query"; import { Query } from "../../common/query";
/**
* Constroller responsable for data part from the API. In other
* words, controller responsable for reading data in BlenDB.
*/
export class DataCtrl {