diff --git a/.gitignore b/.gitignore index 41a58c34bc05b2d998edb628740a3f532d49d164..a8a94969ed216f4424ea9adfde7825be8b619bda 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ /database/views /service schema.sql +/doc/code diff --git a/package.json b/package.json index 7373139ac4a320a33fe2501ff765505e024e75b7..4e1f0b91236363a44e497ee277201504d9f2692c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "show-coverage": "xdg-open coverage/lcov-report/index.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", - "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": { "type": "git", @@ -42,6 +43,7 @@ "raml2html": "^3.0.1", "supertest": "^3.0.0", "tslint": "^3.15.1", - "tslint-stylish": "^2.1.0-beta" + "tslint-stylish": "^2.1.0-beta", + "typedoc": "^0.11.1" } } diff --git a/src/adapter/monet.ts b/src/adapter/monet.ts index 68d7ca3146e74c11a69eaaf3cb0bc6f23c8ebef6..5675b014c3290b8718664dfb5b4d5f6e9f4637f8 100644 --- a/src/adapter/monet.ts +++ b/src/adapter/monet.ts @@ -23,33 +23,73 @@ import { View } from "../core/view"; import { Source } from "../core/source"; import { FilterOperator } from "../core/filter"; import { DataType } from "../common/types"; +/** @hidden */ const MDB = require("monetdb")(); +/** + * Params required to connect with a MonetDB database and + * to create a MonetAdapter object. + */ export interface MonetConfig { + /** Database hostname */ host: string; + /** Database port */ port: number; + /** Database name */ dbname: string; + /** Database user */ user: string; + /** Datavase password */ password: string; } - +/** + * Represent the data format returned by MonetDB. + * This interface is used to parse this format to the BlenDB Standart. + */ interface MonetResult { + /** Query result as a list of values */ data: any[]; + /** Meta data of each attribute required. */ structure: {type: string, column: string, index: number}[]; } +/** + * Adapter which connects with a MonetDB database. + */ export class MonetAdapter extends SQLAdapter { + /** Information used to connect with a MonetDB database. */ 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) { super(); 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 { const query = this.getQueryFromView(view); 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 { let pool: any = new MDB(this.config); pool.connect(); @@ -79,15 +119,31 @@ export class MonetAdapter extends SQLAdapter { pool.close(); } + /** + * Materialize a given view. + * @param view - View to be materialized. + */ public materializeView(view: View): boolean { 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 { const query = this.getQueryFromSource(source, data); 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 { switch (dt) { case DataType.DATE: @@ -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 { switch (op) { case FilterOperator.EQUAL: diff --git a/src/adapter/postgres.ts b/src/adapter/postgres.ts index ce0168bd7922664f3af5761f6ed20b107d203a66..3455dcd6f0dd22fa6d8109f266456c660cbe1517 100644 --- a/src/adapter/postgres.ts +++ b/src/adapter/postgres.ts @@ -25,18 +25,41 @@ import { FilterOperator } from "../core/filter"; import { Pool, PoolConfig } from "pg"; import { DataType } from "../common/types"; +/** Adapter which connects with a PostgreSQL database. */ export class PostgresAdapter extends SQLAdapter { + /** Information used to connect with a PostgreSQL database. */ 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) { super(); 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 { const query = this.getQueryFromView(view); 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{ this.pool.connect((err, client, done) => { if (err) { @@ -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 { const query = this.getQueryFromSource(source, data); this.executeQuery(query, cb); } + /** + * Materialize a given view. + * @param view - View to be materialized. + */ public materializeView(view: View): boolean { 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 { switch (dt) { case DataType.DATE: @@ -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 { switch (op) { case FilterOperator.EQUAL: diff --git a/src/adapter/sql.ts b/src/adapter/sql.ts index 4c10cd3168401f38a9808b1c03de297517f47f78..b54fdab2a9d594667ca0f850cf89780e327e77ea 100644 --- a/src/adapter/sql.ts +++ b/src/adapter/sql.ts @@ -28,29 +28,67 @@ import { AggregationType, RelationType, DataType } from "../common/types"; import { Operation, Opcode } from "../common/expression"; import { View } from "../core/view"; +/** + * Information required to make a join clause. + * The dimension and views that contain this dimension. + */ interface DimInfo { + /** Dimension object. */ dim: Dimension; + /** Set of views that contain the dimension. */ views: View[]; } +/** + * Several translations of a dimension that will be used in different + * parts of a query + */ interface DimTranslation { + /** Translation with the AS clause. Used on SELECT clause. */ aliased: string; + /** Translation without the AS clause. USED on GROUP BY clause. */ noalias: string; + /** The alias of the dimension. The quoted name. */ alias: string; + /** Expansion of a sub dimension. */ expanded: boolean; } +/** Translated view, the query which gets all data and its name */ interface QueryAndName { + /** Query that returns all the data of a view. */ query: string; + /** The view name. */ 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 { + /** Dictionary indexed by dimension name that returns 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}; } +/** + * 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 { + /** + * Translate a view to a SQL query. + * @param view - View to be translated. + */ public getQueryFromView(view: View): string { const partials = this.buildPartials(view).filter((i) => { return i.query !== ""; @@ -76,6 +114,13 @@ export abstract class SQLAdapter extends Adapter { 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[] { let op = view.operation; let queue: View[] = op.values.map((i) => i); @@ -104,10 +149,19 @@ export abstract class SQLAdapter extends Adapter { 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 { 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 { switch (op.opcode) { case Opcode.REDUCE: @@ -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 { // Mapping, which views the metrics and dimensions are const mapping = this.buildMaps(partials); @@ -183,6 +243,11 @@ export abstract class SQLAdapter extends Adapter { 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 { let dimMap: {[key: string]: DimInfo} = {}; let nameMap: {[key: string]: View} = {}; @@ -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[] { let parentClauses: Clause[] = []; for (let i = 0; i < partials.length; ++i) { @@ -228,6 +300,12 @@ export abstract class SQLAdapter extends Adapter { 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 { switch (aggrType) { case AggregationType.SUM: @@ -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 { switch (relation) { case RelationType.DAY: @@ -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 { /* This adapter uses the concept of functions in Postgres to @@ -278,11 +368,22 @@ export abstract class SQLAdapter extends Adapter { 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 { const quotedName = "\"" + item.name + "\""; 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 { const r = clause.filters.map((item) => { return this.translateFilter(item, map); @@ -292,6 +393,12 @@ export abstract class SQLAdapter extends Adapter { 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 { if (!map[filter.target.name]) { return ""; @@ -304,6 +411,11 @@ export abstract class SQLAdapter extends Adapter { 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 { const func = this.getAggregateFunction(metric.aggregation, view.origin); const quotedName = "\"" + metric.name + "\""; @@ -311,6 +423,13 @@ export abstract class SQLAdapter extends Adapter { 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, ancestor: Dimension, view: View): DimTranslation { @@ -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 { let consult: string; let colums: any[] = []; @@ -349,7 +473,17 @@ export abstract class SQLAdapter extends Adapter { 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; - + /** + * 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; } diff --git a/src/api/controllers/collect.ts b/src/api/controllers/collect.ts index 8ac5b57f8a9ba90d0dc07b1c058b61c796c2afda..e36ca6f2dc99800cd28ca743ed0acc751227a356 100644 --- a/src/api/controllers/collect.ts +++ b/src/api/controllers/collect.ts @@ -24,11 +24,28 @@ import { Source, Field } from "../../core/source"; import { EnumType } from "../../core/enumType"; 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{ [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 { + /** + * 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) { const validador: Valid = { diff --git a/src/api/controllers/data.ts b/src/api/controllers/data.ts index add1317f681f8701b1b5b3991a2cd6c883342119..a9cf36c812bc669d3660e93105f2df7a55483889 100644 --- a/src/api/controllers/data.ts +++ b/src/api/controllers/data.ts @@ -22,7 +22,18 @@ import * as express from "express"; import { Request } from "../types"; 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 { + /** + * Route that validates a query and returns the query 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 read(req: Request, res: express.Response, next: express.NextFunction) { let metrics = req.query.metrics.split(",").filter((item: string) => item !== ""); let dimensions = req.query.dimensions.split(",").filter((item: string) => item !== ""); diff --git a/src/api/controllers/engine.ts b/src/api/controllers/engine.ts index af65355702dafde30a238b73bd163593f3cd102a..4fde6269537dca47013104a91200a53527965e91 100644 --- a/src/api/controllers/engine.ts +++ b/src/api/controllers/engine.ts @@ -21,19 +21,52 @@ import * as express from "express"; import { Request } from "../types"; +/** + * Constroller responsable for the static part from the API. In other + * words, controller responsable for return the meta data stored in the + * engine object that API users can use to create queries. + */ export class EngineCtrl { + /** + * Route that returns the list of available metrics. + * @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 metrics(req: Request, res: express.Response, next: express.NextFunction) { res.status(200).json(req.engine.getMetricsDescription()); } + /** + * Route that returns the list of available dimensions. + * @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 dimensions(req: Request, res: express.Response, next: express.NextFunction) { res.status(200).json(req.engine.getDimensionsDescription()); } + /** + * Route that returns the list of available enumerable types. + * @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 enumTypes(req: Request, res: express.Response, next: express.NextFunction) { res.status(200).json(req.engine.getEnumTypesDescription()); } + /** + * Route that returns the list of available data sources for insertion. + * @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 sources(req: Request, res: express.Response, next: express.NextFunction) { res.status(200).json(req.engine.getSourcesDescription()); } diff --git a/src/api/middlewares/adapter.ts b/src/api/middlewares/adapter.ts index 1e4bcbf0725a3ffcc74dd0d94886f898a525f944..b8f06214f384da1af1db484a84ea065b1268587a 100644 --- a/src/api/middlewares/adapter.ts +++ b/src/api/middlewares/adapter.ts @@ -25,6 +25,11 @@ import { MonetAdapter, MonetConfig } from "../../adapter/monet"; import { PoolConfig } from "pg"; import { Connection } from "../../util/configParser"; +/** + * Creates a PostgreSQL adapter and middleware that + * inserts the adapter into the request objects. + * @param config - Parameters required to connect in database. + */ export function PostgresMw(config: Connection): Middleware { let parsedConfig: PoolConfig = { user: config.user, @@ -43,6 +48,11 @@ export function PostgresMw(config: Connection): Middleware { } +/** + * Creates a MonetDB adapter and middleware that + * inserts the adapter into the request objects. + * @param config - Parameters required to connect in database. + */ export function MonetMw(config: Connection): Middleware { let parsedConfig: MonetConfig = { user: config.user, diff --git a/src/api/middlewares/engine.ts b/src/api/middlewares/engine.ts index 9bc6b16a2c3a978961301fc6b690d842c06a3568..bec7ae41b46b62eb470d2d9a08079241e5238f5c 100644 --- a/src/api/middlewares/engine.ts +++ b/src/api/middlewares/engine.ts @@ -22,6 +22,11 @@ import { Engine } from "../../core/engine"; import { ParsedConfig } from "../../util/configParser"; import { Middleware } from "../types"; +/** + * Creates a engine and middleware that + * inserts the engine into the request objects. + * @param config - Parsed database schema. + */ export function EngineMw (config: ParsedConfig): Middleware { let engine: Engine = new Engine(); diff --git a/src/api/middlewares/error.ts b/src/api/middlewares/error.ts index a35bb41e8b2ca7d71c18253b8b252201e6685ac7..1bb7f1d6c22d50e1364c66beb081ab2747f234e2 100644 --- a/src/api/middlewares/error.ts +++ b/src/api/middlewares/error.ts @@ -20,6 +20,10 @@ import * as express from "express"; +/** + * Creates a middleware to handle errors proper to each API version. + * @param config - API version. + */ export function ErrorMw(config: string): express.ErrorRequestHandler { const handlers: { [key: string]: express.ErrorRequestHandler } = { "v1": function(err, req, res, next) { diff --git a/src/api/router-v1.ts b/src/api/router-v1.ts index c9b565176175ada4a5b7f2e6d88d2ec32f792562..ca23e6db304713d1c9c1fdfd9b0a50b3b1a928c6 100644 --- a/src/api/router-v1.ts +++ b/src/api/router-v1.ts @@ -18,6 +18,7 @@ * along with blendb. If not, see <http://www.gnu.org/licenses/>. */ +/** @hidden */ const osprey = require("osprey"); // import controllers @@ -25,6 +26,7 @@ import { DataCtrl } from "./controllers/data"; import { CollectCtrl } from "./controllers/collect"; import { EngineCtrl } from "./controllers/engine"; +/** @hidden */ export const router = osprey.Router(); router.get("/metrics", EngineCtrl.metrics); diff --git a/src/api/types.ts b/src/api/types.ts index eb643c32b1bd20d4d5585aa3d3605819455f10e2..0fe099f9690f47667205d7bb723da6168f9b984b 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -22,11 +22,26 @@ import * as express from "express"; import { Engine } from "../core/engine"; import { Adapter} from "../core/adapter"; +/** + * Extension of Express requests that suports the addition + * of an engine and an adapter. + * This extension is required because some Middlewares + * add some objetcs, in this case metrics and dimensions, + * to the Request object. To typescript compiler do not + * return a error the extension must be made. + */ export interface Request extends express.Request { + /** A engine object. Represents the database in BlenDB perspective. */ engine: Engine; + /** A adapter object. Used to communicate with the database in use. */ adapter: Adapter; } +/** + * Extension Middleware function of ExpressJS Module. + * Uses the custom Request object and is used to define + * the middlewares of BlenDB API. + */ export interface Middleware { (req: Request, res: express.Response, next: express.NextFunction): void; } diff --git a/src/common/expression.ts b/src/common/expression.ts index 513203e49a174da879617d657bd190c71fdef28c..8a9ac07029ed01b7fa893ce3b282c106e8dbbf85 100644 --- a/src/common/expression.ts +++ b/src/common/expression.ts @@ -20,13 +20,41 @@ import { View } from "../core/view"; +/** + * Operation codes to each allowed operation over a view. + */ export enum Opcode { + /** + * Push operation. + * This operation means that the view is materialized. + * This also means that there are no need to create it + * using other views. + * And also means that this view is a valid data location. + */ PUSH, + /** + * Join operation. + * The view will be created using a set of others view. + * This operation is equivalent a INNER JOIN of SQL + * or as close as possible of JOIN. + */ JOIN, + /** + * Reduce operation. + * The reduce operation removes irrelevant metrics and + * dimensions and apply clauses. Represents the Projection + * and Selecion operations of Relational Algebra. + */ REDUCE } +/** + * Defines how to construct a View with a operation + * and a set of chidren Views. + */ export interface Operation { + /** Operation code used. */ opcode: Opcode; + /** Set of views required to perform the operation. */ values: View[]; } diff --git a/src/common/query.ts b/src/common/query.ts index fd7f646e012dbd213a65fe5c0949e79fa83827b6..4cb0a9e986b6e183287d5bb2e279637080a1095a 100644 --- a/src/common/query.ts +++ b/src/common/query.ts @@ -22,9 +22,16 @@ import { Metric } from "../core/metric"; import { Dimension } from "../core/dimension"; import { Clause } from "../core/clause"; +/** + * Internal representation of a query in BlenDB perspective. + */ export interface Query { - public metrics: Metric[]; - public dimensions: Dimension[]; - public clauses?: Clause[]; - public sort?: (Metric|Dimension)[]; + /** Set of metrics of the query. */ + metrics: Metric[]; + /** Set of dimensions of the query. */ + dimensions: Dimension[]; + /** Set of clauses of the query. */ + clauses?: Clause[]; + /** List of metrics and dimensions to sort the query. */ + sort?: (Metric|Dimension)[]; } diff --git a/src/common/types.ts b/src/common/types.ts index 0bcf1498bbce7cf2a08bfbc4aca681b7f0e81fe3..c9e5172a9dd9e635d568b963c6a0ac8cbb44c5e1 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -18,28 +18,71 @@ * along with blend. If not, see <http://www.gnu.org/licenses/>. */ +/** + * Available aggregation function to metrics. + */ export enum AggregationType { - NONE, - SUM, - AVG, - COUNT, - MAX, - MIN + /** Used as error code. So suitable aggregation found. */ + NONE, + /** Sum aggregation function. Sum of all registers. */ + SUM, + /** Average aggregation function. Average of all registers. */ + AVG, + /** Count agggreation function. Count the number of registers. */ + COUNT, + /** Maximum aggregation function. The biggest value of all registers. */ + MAX, + /** Minimum aggregation function. The smallest value of all registers. */ + MIN }; +/** + * Available relationships between sub dimensions and its parents. + */ export enum RelationType { - NONE, - DAY, - MONTH, - YEAR, + /** Used when there are no relation. The dimension is not a sub dimension. */ + NONE, + /** + * Day relation. + * The parent is a timestamp and the dimension is only the day of + * the timestamp. + */ + DAY, + /** + * Month relation. + * The parent is a timestamp and the dimension is only the month of + * the timestamp. + */ + MONTH, + /** + * Year relation. + * The parent is a timestamp and the dimension is only the year of + * the timestamp. + */ + YEAR, }; +/** + * Available data types for metrics, dimensions and source members. + * In other words types that BlenDB can handle. + */ export enum DataType { + /** Used as error code when no suitable type is found. */ NONE, + /** Interger type. */ INTEGER, + /** Floating number type. */ FLOAT, + /** String type. */ STRING, + /** Timestamp type. ISO format */ DATE, + /** True/False type. */ BOOLEAN, - ENUMTYPE, + /** + * Enumerable type. + * Custom type, that only accepts a set of previeous defined values. + * Custom enumeration defined in configuration file. + */ + ENUMTYPE }; diff --git a/src/core/adapter.ts b/src/core/adapter.ts index d70f56151307c936d1c183b5855d9c29742252e2..9929807ccdaf598723773b2e9dfb830dae4c0d89 100644 --- a/src/core/adapter.ts +++ b/src/core/adapter.ts @@ -21,8 +21,32 @@ import { View } from "./view"; import { Source } from "./source"; +/** + * Absraction of the SGBD from the BlenDB perspective. Used to + * perform all the operations into the database that the Blendb + * requires. This operations include read and write data. + */ export abstract class Adapter { + /** + * 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 abstract getDataFromView(view: View, cb: (err: Error, result: any[]) => void): void; + /** + * Materialize a given view. + * @param view - View to be materialized. + */ public abstract materializeView(view: View): boolean; + /** + * 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 abstract insertIntoSource(source: Source, data: any[], cb: (err: Error, result: any[]) => void): void; } diff --git a/src/core/clause.ts b/src/core/clause.ts index 1b7c47e6f47d07586f36f40b0e7b3a657bdc06ae..5e70f6bc2b72e52353bd9554fa408b62b6b370e3 100644 --- a/src/core/clause.ts +++ b/src/core/clause.ts @@ -23,15 +23,25 @@ import { Hash } from "../util/hash"; import { Dimension } from "./dimension"; import { Metric } from "./metric"; +/** Parameters used to create a Clause object. */ export interface ClauseOptions { + /** Set of filters that form the clause. */ filters: Filter[]; } +/** Set of restrictions applied to a query united by the operator OR. */ export class Clause { + /** Hash of components that unique identify the clause. */ public readonly id: string; + /** Set of filters the form the clause. */ public readonly filters: Filter[]; + /** Set of attributes affected by this clause. */ public readonly targets: (Metric|Dimension)[]; + /** + * Create a clause object. + * @param options - Parameters required to create a clause object. + */ constructor (options: ClauseOptions) { this.filters = options.filters.map((i) => i); const t = this.filters.map((i) => i.target).sort((a, b) => { @@ -52,6 +62,14 @@ export class Clause { this.id = Hash.sha1(filtersIds.sort()); } + /** + * Checks if the targets of this clause is a sub set of + * the given coverage set. + * In other words, this clause could be applied to a view + * which has the coverage set as set of metrics and dimensions + * @param coverage - Set of attributes which will be verified if + * contain all targets in the clause. + */ public isCovered(coverage: (Metric|Dimension)[]): boolean { return this.targets.every((i) => coverage.some((j) => i.name === j.name)); } diff --git a/src/core/dimension.ts b/src/core/dimension.ts index 955ee443b4b72c08cf36e51518ac6ef2ea1ecfea..202d5b62522dbffcb2df9de165a8b659ee6f3bf2 100644 --- a/src/core/dimension.ts +++ b/src/core/dimension.ts @@ -21,31 +21,64 @@ import { RelationType, DataType } from "../common/types"; import { EnumType } from "./enumType"; +/** Parameters used to create a Dimension object. */ export interface DimensionOptions { + /** Dimension name. */ name: string; + /** Dimension data type. */ dataType: DataType; + /** Parent dimension, in case this dimension is a sub dimenion. */ parent?: Dimension; + /** Relationship with the parent. */ relation?: RelationType; + /** Breif description of what this dimension represents. */ description?: string; + /* Enumerable type name, used if data type is enumerable type. */ enumType?: string; } +/** + * Parameters used to define dimension object in the configuration file. + * Also the string description of a dimension. + */ export interface DimensionStrOptions { + /** Dimension name. */ name: string; + /** Dimension data type. */ dataType: string; + /** Parent dimension, in case this dimension is a sub dimenion. */ parent?: string; + /** Relationship with the parent. */ relation?: string; + /** Breif description of what this dimension represents. */ description?: string; } +/** + * One attribute of database that can be read. + * A dimension defines a aspect or characteristic of the data. + * Used in a query as a desired information and is separated from + * the metrics because this two types of attributes behave differently + * in the query. While dimensions are grouped, metrics are aggregated. + */ export class Dimension { + /** Dimension name. */ public readonly name: string; + /** Dimenion data type. */ public readonly dataType: DataType; + /** Parent dimension, in case this dimension is a sub dimenion. */ public readonly parent: Dimension; + /** Relationship with the parent. */ public readonly relation: RelationType; + /** Breif description of what this dimension represents. */ public readonly description: string; + /* Enumerable type name, used if data type is enumerable type. */ public readonly enumType: string; + /** + * Creates a dimension. + * @param options - Parameters required to create a dimension. + */ constructor(options: DimensionOptions) { this.name = options.name; this.dataType = options.dataType; @@ -55,6 +88,10 @@ export class Dimension { this.enumType = (options.enumType) ? (options.enumType) : ""; } + /** + * Creates a object with the same options used to create this + * dimension as strings. Used to inform the API users. + */ public strOptions(): DimensionStrOptions { if (this.relation === RelationType.NONE) { return { @@ -74,6 +111,10 @@ export class Dimension { } } + /** + * Parse a string to enum(Relation Type). + * @param r - Relation in string format. + */ public static parseRelationType(r: string): RelationType { switch (r) { case "day": @@ -87,6 +128,10 @@ export class Dimension { } } + /** + * Parse an enum(Relation Type) to string. + * @param r - Relation to be stringified. + */ public static stringifyRelationType(r: RelationType): string { switch (r) { case RelationType.DAY: diff --git a/src/core/engine.ts b/src/core/engine.ts index 72a554a47eff5f3e9dac364367647855f02e9ece..712d722082d99f418a587f9dff24baf6b93da6af 100644 --- a/src/core/engine.ts +++ b/src/core/engine.ts @@ -29,23 +29,29 @@ import { EnumType, EnumTypeOptions} from "./enumType"; import { Source , SourceStrOptions } from "./source"; import { ViewHandler } from "../util/viewHandler"; -interface Score { - [key: string]: number; -} - -interface ViewsAndClauses { - views: View[]; - clauses: Clause[]; -} - +/** + * Represents the database schema from the BlenDB perspective. + * The engine is an abstraction of a generic analytics database. + * All the operations that would be realised in the database + * are made through the engine. The main purpose of the engine + * is to translate a localition less query into a localized query. + * In other words, discover in which view a metric/dimension is. + */ export class Engine { + /** Set of views available in the database */ private views: View[] = []; + /** Set of metrics available in the database. */ private metrics: Metric[] = []; + /** Set of enumerable types available in the database. */ private enumTypes: EnumType[] = []; + /** Set of dimensions available in the database. */ private dimensions: Dimension[] = []; + /** Set of sources available in the database. */ private sources: Source[] = []; + /** Graph which represents the database schema. */ private graph: Graph; + /** Constructs an empty database schema. */ constructor () { this.enumTypes = []; this.views = []; @@ -55,26 +61,35 @@ export class Engine { this.graph = new Graph(); } + /** Gets all the available */ public getViews(): View[] { return this.views; } + /** Gets a string description for all the available metrics. */ public getMetricsDescription(): MetricStrOptions[] { return this.metrics.map((i) => i.strOptions()); } + /** Gets a string description for all the available enumerable types. */ public getEnumTypesDescription(): EnumTypeOptions[] { return this.enumTypes.map((i) => i.strOptions()); } + /** Gets a string description for all the available sources. */ public getSourcesDescription(): SourceStrOptions[] { return this.sources.map((i) => i.strOptions()); } + /** Gets a string description for all the available dimensions. */ public getDimensionsDescription(): DimensionStrOptions[] { return this.dimensions.map((i) => i.strOptions()); } + /** + * Adds a new view to the database schema (engine). + * @param view - View to be added. + */ public addView(view: View): View { if (this.graph.addView(view)) { this.views.push(view); @@ -84,16 +99,28 @@ export class Engine { return null; } + /** + * Adds a new enumerable type to the database schema (engine). + * @param enumType - Enumerable type to be added. + */ public addEnumType(enumType: EnumType): EnumType { this.enumTypes.push(enumType); return enumType; } - public addSource(sourc: Source): Source { - this.sources.push(sourc); - return sourc; + /** + * Adds a new source to the database schema (engine). + * @param source - Source to be added. + */ + public addSource(source: Source): Source { + this.sources.push(source); + return source; } + /** + * Adds a new metric to the database schema (engine). + * @param metric - Metric to be added. + */ public addMetric(metric: Metric): Metric { if (this.graph.addMetric(metric)) { this.metrics.push(metric); @@ -103,6 +130,10 @@ export class Engine { return null; } + /** + * Gets the metric object given its name. + * @param name - Metric's name. + */ public getMetricByName(name: string): Metric { let result = this.metrics.find(metric => metric.name === name); @@ -113,6 +144,10 @@ export class Engine { return result; } + /** + * Gets the enumerable type object given its name. + * @param name - Enumerable type name. + */ public getEnumTypeByName(name: string): EnumType { let result = this.enumTypes.find(EnumType => EnumType.name === name); @@ -123,6 +158,10 @@ export class Engine { return result; } + /** + * Gets the source object given its name. + * @param name - Source's name. + */ public getSourceByName(name: string): Source { let result = this.sources.find(source => source.name === name); @@ -133,6 +172,10 @@ export class Engine { return result; } + /** + * Adds a new dimension to the database schema (engine). + * @param dimension - Dimension to be added. + */ public addDimension(dimension: Dimension): Dimension { if (this.graph.addDimension(dimension)) { this.dimensions.push(dimension); @@ -142,6 +185,10 @@ export class Engine { return null; } + /** + * Gets the dimension object given its name. + * @param name - Dimension's name. + */ public getDimensionByName(name: string): Dimension { let result = this.dimensions.find(dimension => dimension.name === name); @@ -152,6 +199,10 @@ export class Engine { return result; } + /** + * Translate a string clause into a clause object. + * @param strClause - Clause in string format. + */ public parseClause(strClause: string): Clause { let strFilters = strClause.split(",").filter((item: string) => item !== ""); let filters: Filter[] = []; @@ -162,6 +213,10 @@ export class Engine { return new Clause({filters: filters}); } + /** + * Translate a string filter into a clause object. + * @param strFilter - Filter in string format. + */ public parseFilter(strFilter: string): Filter { let segment = Filter.segment(strFilter); if (segment) { @@ -202,10 +257,25 @@ export class Engine { throw new Error("Filter could not be created: Operator on \"" + strFilter + "\" could not be extracted"); } } + + /** + * Transform a query (location less) into a view (localized). + * The main difference between a query object and a view object + * is that a view object can be built from other view objects + * and construct a valid query to conventional SGBD's. + * The query object do not have any location, so can not be + * translate in a valid SGBD query. In other words this method + * find a location (FROM clause in SQL) to the query object. + * @param q - Query to be located. + */ public query (q: Query): View { return this.selectOptimalView(q); } + /** + * Selects the best location (view) to a given query. + * @param q - Query to be located. + */ private selectOptimalView (q: Query): View { let queries: Query[] = []; if (q.metrics.length > 0) { @@ -236,6 +306,17 @@ export class Engine { } + /** + * Finds a set of views that has 2 properties: + * 1- Contain all the attributes (metrics/dimensions) + * required do make the query. + * 2- The set of views is connected. This means that + * if the set has more than one view is possible to take + * any view in the set exists another view that share + * at least one dimension. This meke possible to join + * views. + * @param q - Query to be covered. + */ private getCover (q: Query): View[] { const optimalViews = this.graph.cover(q); if (optimalViews.length === 0) { diff --git a/src/core/enumType.ts b/src/core/enumType.ts index bfb5d975b2e57c596554bf0ef96578862c35ab37..e65d78c77ffc9295c8bf07f98507e79f3261e7d4 100644 --- a/src/core/enumType.ts +++ b/src/core/enumType.ts @@ -20,25 +20,47 @@ import { DataType } from "../common/types"; +/** Parameters used to create a Enumerable type object. */ export interface EnumTypeOptions { + /** The type name. */ name: string; + /** The allowed values to this type. */ values: string[]; } + +/** + * Defines a custom data type. + * This custom type has one restriction. Only a finite + * set o values is allowed to this type. All the + * valid values to this type are set in its definition. + */ export class EnumType { + /** The type name. */ public name: string; + /** The allowed values to this type. */ public values: string[]; + /** + * Create a enumerable type definition. + * @param options - Parameters required to create a enumerable type. + */ constructor(options: EnumTypeOptions) { this.name = options.name; this.values = options.values; } + /** Gets a string description of this enumerable type. */ public strOptions(): EnumTypeOptions { return{ name: this.name, values: this.values }; } + + /** + * Parse an enum(Data type) to string. + * @param a - Data type to be stringified. + */ public static stringfyDataType(a: DataType): string { switch (a) { case DataType.INTEGER: @@ -56,6 +78,10 @@ export class EnumType { } } + /** + * Parse a string to enum(Data Type). + * @param str - Data type in string format. + */ public static parseDataType (str: string): DataType { str = str.toLocaleLowerCase(); switch (str) { diff --git a/src/core/filter.ts b/src/core/filter.ts index 47ec8e15c71db3b3ffba04c6c7694d86a03b9685..624a3f1c53163ee360c4381dbed021dbadf28a32 100644 --- a/src/core/filter.ts +++ b/src/core/filter.ts @@ -23,35 +23,63 @@ import { Metric } from "./metric"; import { Hash } from "../util/hash"; import { DataType } from "../common/types"; +/** Parameters used to create a filter object. */ export interface FilterOptions { + /** Metric/Dimension that will be filtered. */ target: Metric|Dimension; + /** Operation applied to filter. */ operator: FilterOperator; + /** Constant value to be compared. */ value: string; } +/** + * Parameters used to define filter object in the configuration file. + * Also the string description of a filter. + */ export interface StrFilterOptions { + /** Metric/Dimension that will be filtered. */ target: string; + /** Operation applied to filter. */ operator: string; + /** Constant value to be compared. */ value: string; } +/** Available operations to be used in filters */ export enum FilterOperator { + /** Used as error code. So suitable operation found. */ NONE, + /** Equality (==). */ EQUAL, + /** Not equality (!=). */ NOTEQUAL, + /** Greater than (>). */ GREATER, + /** Lower than (<). */ LOWER, + /** Greater or equal than( >=). */ GREATEREQ, + /** Lower or equal than (<=). */ LOWEREQ } export class Filter { + /** Hash of components that unique identify the filter. */ public readonly id: string; + /** Metric/Dimension that will be filtered. */ public readonly target: Metric|Dimension; + /** Constant value to be compared. */ public readonly operator: FilterOperator; + /** Constant value to be compared. */ public readonly value: string; + /** Constant that informs if the target type allow the operator. */ public readonly isValid: boolean; + /** + * Create a filter. + * @param options - Parameters required to create a filter. + */ constructor (options: FilterOptions) { this.target = options.target; this.operator = options.operator; @@ -60,6 +88,10 @@ export class Filter { this.isValid = Filter.isTypeValid(options); } + /** + * Parse a string to enum(Filter Operator). + * @param op - Filter operator in string format. + */ public static parseOperator(op: string): FilterOperator { switch (op) { case "==": @@ -79,6 +111,10 @@ export class Filter { } } + /** + * Splits a continuous string in the string representation of the filter. + * @param strFilter - Filter in string format. + */ public static segment(strFilter: string): StrFilterOptions { for (let i = 0; i < strFilter.length; ++i) { switch (strFilter[i]){ @@ -138,6 +174,10 @@ export class Filter { return null; } + /** + * Verify if the target allows the operation. + * @param op - Operator to be validated. + */ private static isTypeValid(op: FilterOptions): boolean { if (op.operator === FilterOperator.NONE) { return false; diff --git a/src/core/metric.ts b/src/core/metric.ts index 441b90cf29f517c83489427cab36e3191377559d..75ad55d05e0e6b0d106cca42d4542bf85217d378 100644 --- a/src/core/metric.ts +++ b/src/core/metric.ts @@ -21,26 +21,54 @@ import { AggregationType, DataType } from "../common/types"; import { EnumType } from "./enumType"; +/** Parameters used to create a metric object. */ export interface MetricOptions { + /** Metric name. */ name: string; + /** Aggregation function applied on this metric */ aggregation: AggregationType; + /** Metric data type. */ dataType: DataType; + /** Breif description of what this metric represents. */ description?: string; } +/** + * Parameters used to define metric object in the configuration file. + * Also the string description of a metric. + */ export interface MetricStrOptions { + /** Metric name. */ name: string; + /** Aggregation function applied on this metric */ aggregation: string; + /** Metric data type. */ dataType: string; + /** Breif description of what this metric represents. */ description?: string; } +/** + * One attribute of database that can be read. + * A metric defines a numeric agggregated aspect of the data. + * Used in a query as a desired information and is separated from + * the dimension because this two types of attributes behave differently + * in the query. While metrics are aggregated, dimensions are grouped. + */ export class Metric { + /** Metric name. */ public readonly name: string; + /** Aggregation function applied on this metric */ public readonly aggregation: AggregationType; + /** Metric data type. */ public readonly dataType: DataType; + /** Breif description of what this metric represents. */ public readonly description: string; + /** + * Create a metric. + * @param options - Parameters required to create a metric. + */ constructor(options: MetricOptions) { this.name = options.name; this.aggregation = options.aggregation; @@ -48,6 +76,10 @@ export class Metric { this.description = (options.description) ? options.description : ""; } + /** + * Created a object with the same options used to create this + * metric as strings. Used to inform the API users. + */ public strOptions(): MetricStrOptions { return { name: this.name, @@ -57,6 +89,10 @@ export class Metric { }; } + /** + * Parse an enum(Aggregation Type) to string. + * @param a - Aggregation function to be stringified. + */ public static stringifyAggrType(a: AggregationType): string { switch (a) { case AggregationType.SUM: @@ -74,6 +110,10 @@ export class Metric { } } + /** + * Parse a string to enum(Aggregation Type). + * @param str - Aggregation function in string format. + */ public static parseAggrType (str: string): AggregationType { switch (str) { case "sum": diff --git a/src/core/source.ts b/src/core/source.ts index fcd55193d6133f554bf92763076faf7df1e51888..7a04687fd169aff80a284e6bb84ea5fa2575962b 100644 --- a/src/core/source.ts +++ b/src/core/source.ts @@ -21,36 +21,70 @@ import { EnumType } from "./enumType"; import { DataType } from "../common/types"; +/** Attribute of a source. */ export interface Field { + /** Field name. */ name: string; + /** Brief description of what this field represent. */ description?: string; + /* Field data type. */ dataType: DataType; + /* Enumerable type name, used if data type is enumerable type. */ enumType?: string; } +/** Field definition used in configuration file. */ export interface FieldStr { + /** Field name. */ name: string; + /** Brief description of what this field represent. */ description?: string; + /* Field data type. */ dataType: string; } +/** Parameters used to create a source object. */ export interface SourceOptions { + /** Source name. */ name: string; + /** Brief description of what this source represent. */ description?: string; + /** Set of fields that define the attributes of this source. */ fields: Field[]; } +/** + * Parameters used to define source object in the configuration file. + * Also the string description of a source. + */ export interface SourceStrOptions { + /** Source name. */ name: string; + /** Brief description of what this field represent. */ description?: string; + /** Set of fields that define the attributes of this source. */ fields: FieldStr[]; } +/** + * A definition of a "object" that can be inserted into BlenDB. + * To perform a insertion in the database is required to know "what" + * is being inserted. This "what" is a source object that is + * nothing more than a set o attributes (called fields) and + * a identifier (called name). + */ export class Source { + /** Source name. */ public readonly name: string; + /** Brief description of what this field represent. */ public readonly description: string; + /** Set of fields that define the attributes of this source. */ public readonly fields: Field[]; + /** + * Create a source. + * @param options - Parameters required to create a source. + */ constructor(options: SourceOptions) { this.name = options.name; this.description = (options.description) ? options.description : ""; @@ -64,6 +98,10 @@ export class Source { }); } + /** + * Created a object with the same options used to create this + * source as strings. Used to inform the API users. + */ public strOptions(): SourceStrOptions { return { name: this.name, @@ -72,6 +110,11 @@ export class Source { }; } + /** + * Created a set of objects with the same options used to create the + * given fields as strings. Used to inform the API users. + * @param opts - Set of fields to be put in string struct. + */ public static stringfyFieldDataType(opts: Field[]): FieldStr[] { let str: FieldStr[]; str = opts.map((i) => { @@ -84,6 +127,10 @@ export class Source { return str; } + /** + * Created a set of fields given its options. + * @param opts - Set of fields in string struct. + */ public static parseFieldDataType(opts: FieldStr[]): Field[] { let str: Field[]; str = opts.map((i) => { diff --git a/src/core/view.ts b/src/core/view.ts index b1cba66060f122b4bb0af42c240f0c6102070b11..6d15a1dbe9fe4b75fb19a181b09478dbd423e202 100644 --- a/src/core/view.ts +++ b/src/core/view.ts @@ -24,29 +24,55 @@ import { Hash } from "../util/hash"; import { Clause } from "./clause"; import { Operation, Opcode } from "../common/expression"; +/** Information required to load fixture data into the view. */ export interface LoadView { + /** View where the fixture data will be load. */ view: View; + /** Path to a file with fixture data. */ data: string; } +/** Parameters used to create a view object. */ export interface ViewOptions { + /** Set of metrics contained into the view. */ metrics: Metric[]; + /** Set of dimension contained into the view. */ dimensions: Dimension[]; + /** Inform if this view cannot be generated by other views. */ origin: boolean; + /** Set of clauses applied to the view. */ clauses?: Clause[]; + /** List with the sorting order of the attributes. */ sort?: (Metric|Dimension)[]; + /** Definition of how to create this view using other views. */ operation?: Operation; } +/** + * Representation of data location in the BlenDB perspective. + * Readable data are represented by metrics and dimensions and + * these objects can only be found in views. + */ export class View { + /** Hash of components that unique identify the clause. */ public readonly id: string; + /** Set of metrics contained into the view. */ public readonly metrics: Metric[]; + /** Set of dimension contained into the view. */ public readonly dimensions: Dimension[]; + /** Set of clauses applied to the view. */ public readonly clauses: Clause[]; + /** List with the sorting order of the attributes. */ public readonly sort: (Metric|Dimension)[]; + /** Inform if this view cannot be generated by other views. */ public readonly origin: boolean; + /** Definition of how to create this view using other views. */ public readonly operation: Operation; + /** + * Create a view. + * @param options - Parameters required to create a view. + */ constructor (options: ViewOptions) { this.metrics = options.metrics.sort(); this.dimensions = options.dimensions.sort(); diff --git a/src/main.ts b/src/main.ts index 56a5acf4d25df63d151d246a242df35ee023eebe..de792ba500918b67c978c0c1a3bc13716376eaa7 100755 --- a/src/main.ts +++ b/src/main.ts @@ -21,19 +21,24 @@ // external libraries import express = require("express"); +/** @hidden */ const osprey = require("osprey"); +/** @hidden */ const ramlParser = require("raml-parser"); // load router import { router } from "./api/router-v1"; // create a new express app +/** @hidden */ const app = module.exports = express(); // Load configuration file import { ConfigParser } from "./util/configParser"; +/** @hidden */ const configPath = (app.get("env") === "test") ? "config/test.yaml" : "config/config.yaml"; +/** @hidden */ const config = ConfigParser.parse(configPath); // include middlewares diff --git a/src/util/configParser.ts b/src/util/configParser.ts index 87f8ef6e25290307891f3b8f73d2977358e2e090..47cfc4b693b7973a7acc9b8c3f3256aedaf7471b 100644 --- a/src/util/configParser.ts +++ b/src/util/configParser.ts @@ -31,77 +31,138 @@ import { Tsort, TsortDep } from "./tsort"; import * as fs from "fs"; import * as yaml from "js-yaml"; +/** + * Parameters used to define view object in the configuration file. + */ export interface ViewParsingOptions { + /** Fake identifier (human understandable) of the view. */ alias: string; - data: 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; } +/** Information required to conduct fixture loading. */ export interface LoadStruct{ + /** True if is required create structures in the database before loading. */ create: boolean; + /** True any fixture is loaded yet. */ insert: boolean; } +/** Configuration file representation with objects, not strings. */ 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[]; + /** Fixture loading policy. */ struct: LoadStruct; + /** Fixture data configuration. */ 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" @@ -215,6 +276,12 @@ export class ConfigParser { } + /** + * 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 { @@ -267,6 +334,12 @@ export class ConfigParser { 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 = EnumType.parseDataType(opts.dataType); if (type === DataType.NONE) { @@ -300,6 +373,10 @@ export class ConfigParser { }; } + /** + * Parse a metric struct in configuration file. + * @param opts - Metric struct in configuration file. + */ public static parseMetOpts (opts: MetricStrOptions): MetricOptions { let type = EnumType.parseDataType(opts.dataType); if (!(type === DataType.FLOAT || type === DataType.INTEGER)){ @@ -315,6 +392,12 @@ export class ConfigParser { }; } + /** + * 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) => { @@ -330,6 +413,11 @@ export class ConfigParser { 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 = EnumType.parseDataType(opts.fields[k].dataType); @@ -347,6 +435,13 @@ export class ConfigParser { 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) { diff --git a/src/util/graph.ts b/src/util/graph.ts index ad654a93ae4236b32b05ce2a6a0616b1dff89d89..36a5ea1070d368b1b145ba7c9a37393b32b74ddc 100644 --- a/src/util/graph.ts +++ b/src/util/graph.ts @@ -31,75 +31,81 @@ enum State { VISITED } -/* - Class (interface) for a vertex in the graph - Created using a dimension or metric - Several of the attributes are used just for - search proposes like parent and state. -*/ - +/** + * A vertex of a Graph. + * Created using a dimension or metric. + */ interface Vertex { - id: string; // Name of vertex + /** Name of vertex. */ + id: string; + /** Adjacent vertices. */ neighbors: { [key: string]: Edge[] }; - state: State; // During the search the vertex has a state - parent: Vertex; // During the search, what vertex "find" this - isSubDimension: boolean; // During insertion, was subdimension - dimensionParent: Vertex; // If is a subdimension, the vertex for that - dimension: Dimension; // If is a dimension, tha dimension that the vertex represents - metric: Metric; // If is a metric, the metric that the vertexrepresents + /** Vextex state while a search process is performed. */ + state: State; + /** Which vertex "find" this one while a search process is performed. */ + parent: Vertex; + /** Represents a sub-dimension ? */ + isSubDimension: boolean; + /** The parent vertex if this vertex is a sub-dimension vertex. */ + dimensionParent: Vertex; + /** The dimension which this vertex represent. */ + dimension: Dimension; + /** The metric which this vertex represent. */ + metric: Metric; } -/* - Class (interface) dor a edge of the Graph - Normaly is possible to use a list of vertices - to represent the edges, however this edges have some - information different from the vertex, so a class - is needed. - - Edges represent views or parent relationship with - dimensions and subdimensions. -*/ +/** + * A graph's edge. + * Edges represent views or parent relationship with + * dimensions and subdimensions. + */ interface Edge { - isView: boolean; // True if edge represent view - view: View; // The view if isView is True - subDimension: Dimension; // The subDimension if isView is False + /** True if edge represent view. */ + isView: boolean; + /** The view which this edge represents. */ + view: View; + /** The sub-dimension which this edge represents. */ + subDimension: Dimension; } /* - Graph Class - Used to convert the Views Representation (Tables/Relations) - into a graph representation of the database schema. - Is not generic Graph, is not possible create vertex and - edges at will, only with metrics, dimensions and views. - - Struct: - There is a vertex for each Metric and Dimension inserted - in the graph. - The graph is directed, but mostly vertices has edges in - both directions. - There is a edge between 2 vertices i and j in 3 cases: - I) if i and j are in the same view, there is a edge (i, j) - and (j, i) - II) if i is parent of the subdimention j , there is a - edge (i, j) - III) If a view has only one vertex, there is a loop (i, i) - - So there is a edge if vertices are in the same view or - there is a parent relationship between vertices. -*/ + * A graph to represent the database schema. + * Used to convert the Views Representation (Tables/Relations) + * into a graph representation of the database schema. + * Is not generic Graph, is not possible create vertex and + * edges at will, only with metrics, dimensions and views. + * Struct: + * There is a vertex for each Metric and Dimension inserted + * in the graph. + * The graph is directed, but mostly vertices has edges in + * both directions. + * There is a edge between 2 vertices i and j in 3 cases: + * I) if i and j are in the same view, there is a edge (i, j) + * and (j, i) + * II) if i is parent of the subdimention j , there is a + * edge (i, j) + * III) If a view has only one vertex, there is a loop (i, i) + * So there is a edge if vertices are in the same view or + * there is a parent relationship between vertices. + */ export class Graph { - private vertices: Vertex[]; // vertices of the graph + /** Graph's set of vertices. */ + private vertices: Vertex[]; + /** + * Graph's set of vertices in dictonary form. + * Indexed by vertex name. Returns the vertex object. + */ private verticeMap: { [key: string]: Vertex }; - // Map where key is the vertex id and the value is the vertex + /** Creates a graph. */ public constructor () { this.vertices = []; this.verticeMap = {}; } - /* - Adds a metric - returns true if the metric was inserted in the graph - */ + /** + * Adds a metric and inform if the inserion was a success. + * @param met - Metric to be inserted. + */ public addMetric(met: Metric): boolean { if (!met) { return false; @@ -126,10 +132,10 @@ export class Graph { } } - /* - Adds a dimension - returns true if the dimension was inserted in the graph - */ + /** + * Adds a dimension and inform if the inserion was a success. + * @param dim - Dimension to be inserted. + */ public addDimension(dim: Dimension): boolean { if (!dim) { return false; @@ -191,15 +197,15 @@ export class Graph { } } - /* - Add a View - Get the metrics and dimensions of this view - and create edges between then - returns true when the view in inserted (create all the - edges needed) and false otherwise - If returns false any edge was created, if true all edges - were created. - */ + /** + * Add a View + * Get the metrics and dimensions of this view + * and create edges between then + * returns true when the view in inserted (create all the + * edges needed) and false otherwise + * If false is returned any edge is created. + * @param view - View to be inserted. + */ public addView (view: View): boolean { if (!view) { return false; @@ -245,12 +251,15 @@ export class Graph { } - /* - Check if a edge can be create - return false when at least one vertex does not exist in the graph - or the edge already exists - returns true otherwise - */ + /** + * Check if a edge can be create. + * return false when at least one vertex does not exist in the graph + * or the edge already exists + * returns true otherwise + * @param idV - Identifier of one edge's vertex. + * @param idV - Identifier of the other edge's vertex. + * @param value - View represented by this edge. + */ private checkEdge(idV: string, idU: string, value: View): boolean { let v: Vertex = this.verticeMap[idV]; let u: Vertex = this.verticeMap[idU]; @@ -271,18 +280,18 @@ export class Graph { } } - /* - Create a edge between 2 vertices - - Edges are a kind of special, when say create a edge is - in fact put a index on the array of a edge. - - The edges have a array of ids, when you try to create a edge - and the edge doen not exist, create the edge and a array with - a lonely index in it. If the edge exist but the id is not in - the array, adds the id and checkEdge would return true, - if the id already exists return false - */ + /** + * Create a edge between 2 vertices + * Edges are a kind of special, when say create a edge is + * in fact put a index on the array of a edge. + * The edges have a array of ids, when you try to create a edge + * and the edge doen not exist, create the edge and a array with + * a lonely index in it. If the edge exist but the id is not in + * the array, adds the id. + * @param idV - Identifier of one edge's vertex. + * @param idV - Identifier of the other edge's vertex. + * @param value - View represented by this edge. + */ private edge(idV: string, idU: string, value: View): void { // Assuming that checkEdge is called first let v: Vertex = this.verticeMap[idV]; @@ -302,13 +311,13 @@ export class Graph { } } - /* - Given a list of metrics and dimensions returns a set - of Views (children) that can be used to create a query that - returns the data asked. - - If this set cannot be created, throws a error - */ + /** + * Given a list of metrics and dimensions returns a set + * of Views (children) that can be used as "locations" to + * perform the query. + * If this set cannot be created, throws a error. + * @param q - Query to be "localized"(covered). + */ public cover(q: Query): View[] { const metrics = q.metrics; const dimensions = q.dimensions; @@ -438,13 +447,15 @@ export class Graph { return output; } - /* - From a edge, coohse the best view, based on the metric and dimensions - that are not cover yet, return a View. - The algorithm chooses the view that covers more metrics and dimensions - that are not covered yet, if there is a tie chooses the one with - less dimensions, if tie again, the earliest in the list. - */ + /** + * Choose the "best" view in a edge. + * The algorithm chooses the view that covers more metrics and dimensions + * that are not covered yet, if there is a tie chooses the one with + * less dimensions, if tie again, the earliest in the list. + * @param views - Cadidates for best view. + * @param metToCover - Uncovered metrics. + * @param dimToCover - Uncovered dimensions. + */ private pickEdge (views: View[], metToCover: Metric[], dimToCover: Dimension[]): View { // Picks the first option as the best one until now let bestView = views[0]; @@ -495,11 +506,14 @@ export class Graph { return bestView; } - /* - Check if a vertex is can be visited from another vertex. - Basically checks if the vertex is unvisited, if is a sub dimension - or view edge and if is a view, chech the constraints. - */ + /** + * Check if a vertex is can be visited from another vertex. + * Basically checks if the vertex is unvisited, if is a sub dimension + * or view edge and if is a view, check the constraints. + * @param v - Vertex to be visited. + * @param neighbors - Possible choises to reach the vertex v. + * @param clauses - Query clauses. Used as constraints. + */ private canVisit(v: Vertex, neighbors: Edge[], clauses: Clause[] ): boolean { if (v.state !== State.UNVISITED) { return false; @@ -520,9 +534,11 @@ export class Graph { return false; } - /* - Check if a set of filter/clauses of a view suits for the query - */ + /** + * Check if a set of filters/clauses of a view suits for the query + * @param queryClauses - Clauses used in the query. + * @param viewVlauses - Clauses applied to a view. + */ private passConstraints(queryClauses: Clause[], viewClauses: Clause[]): boolean { /* TODO: Enhance constraint check. @@ -597,6 +613,11 @@ export class Graph { } + /** + * Find all metrics and dimension in a query + * and get its correspondent vertex identifiers. + * @param q - Query which contains vertices. + */ private verticesInQuery(q: Query): string[] { const metrics = q.metrics; const dimensions = q.dimensions; diff --git a/src/util/hash.ts b/src/util/hash.ts index 0ca3c606ad88c7d5531fcf525cc80a3129eb31c8..6f7b4e8b722872cb739dde4e255279a97c435b33 100644 --- a/src/util/hash.ts +++ b/src/util/hash.ts @@ -21,6 +21,10 @@ import crypto = require("crypto"); export class Hash { + /** + * Perform a sha1 encryption. + * @param objects - Set of objects to be used to create the hash. + */ public static sha1(...objects: any[]): string { let hash = crypto.createHash("sha1"); diff --git a/src/util/tsort.ts b/src/util/tsort.ts index 273c7ea1b07cc2bd06b0b7b6bdb26cd4e1f73abb..a97f7f4bf2d42721dbb803f5552ab3f22e984f8b 100644 --- a/src/util/tsort.ts +++ b/src/util/tsort.ts @@ -18,20 +18,47 @@ * along with blendb. If not, see <http://www.gnu.org/licenses/>. */ +/** + * A representation of a dependency relation. + * Equivalent to a edge in a dependency graph. + */ export interface TsortDep { + /** The value (vertex). */ value: string; + /** The value which this depends of. */ dependOf?: string; } +/** + * A dependency graph. + */ interface Graph { + /** + * Dictionary indexed by vertices which + * returns a list of the adjacent vertex. + */ [key: string]: string[]; } interface VertexMark { + /** + * Dictionary indexed by vertices which + * checks if the vertex is already sorted. + */ [key: string]: boolean; } +/** + * Dependency sorting methods. Creates a dependency + * graph and use the topological sorting algorithm + * putting all the dependencies before the dependent. + */ export class Tsort { + /** + * Topologicaly sort strings in the TSortDep format. + * @param deps - Dependency relations (edges in the + * dependency graph). + */ public static dependencies(deps: TsortDep[]): string[]{ let graph: Graph = {}; let mark: VertexMark = {}; @@ -64,6 +91,14 @@ export class Tsort { return output; } + /** + * Recursive topological sorting algorithm strarting from + * a given vertex + * @param vertex - Topological sorting root. + * @param graph - Dependency graph. + * @param mark - Mark indicatiing if the vertex is already sorted. + * @param output - The topological sorting. + */ private static markVertex(vertex: string, graph: Graph, mark: VertexMark, output: string[]): void { if (mark[vertex]) { diff --git a/src/util/viewHandler.ts b/src/util/viewHandler.ts index 9e3aea11348b477f03048b3d805dc240e5f5b5d6..0e0252df1bc459e3db18bd4532e0d33dcfd4d3f3 100644 --- a/src/util/viewHandler.ts +++ b/src/util/viewHandler.ts @@ -25,16 +25,35 @@ import { Query } from "../common/query"; import { Dimension } from "../core/dimension"; import { Metric } from "../core/metric"; +/** + * Dictionary with relevancy for each dimension. + * Indexed by dimention name and returns the score. + */ interface Score { [key: string]: number; } +/** Result of a join/reduction view operation. */ interface ViewsAndClauses { + /** Set of views returned after operation. */ views: View[]; + /** Set of clauses not applied yet. */ clauses: Clause[]; } +/** + * View's handler. Perform several operations with + * views and used to manipulate and create new views + * in a safe way. + */ export class ViewHandler { + /** + * Perform the JOIN operation to create a new View + * @param q - Query with the parameters of the view + * to be created. + * @param views - Set of views required to create + * this view with the JOIN operation. + */ public static queryJoin(q: Query, views: View[]): View { if (views.length > 1) { return new View({ @@ -55,6 +74,12 @@ export class ViewHandler { } } + /** + * Perform the REDUCE operation to create a new View + * @param q - Query with the parameters of the view + * to be created. + * @param view - View which the REDUCE was applied. + */ public static queryReduce(q: Query, view: View): View { const v = new View({ metrics: q.metrics, @@ -71,6 +96,14 @@ export class ViewHandler { return (v.id !== view.id) ? v : view; } + /** + * Constructs one view which represents a query. + * Given a set of views that cover the query + * combines its elements using JOIN and REDUCE + * operations to create a unique view. + * @param q - Query which represents the view to be created + * @param views - Views which cover the query. + */ public static growView(q: Query, views: View[]): View { let clausesToCover = q.clauses.map((i) => i); let metView: View = null; @@ -126,6 +159,12 @@ export class ViewHandler { } } + /** + * Calculate the score of a set of dimensions. + * @param views - Views which contain the dimensions to be scored. + * @param clauses - Clauses which contain the dimensions to be scored. + * @param dimensions - Dimensions in a query. These receives a extra score. + */ private static dimensionsScore(views: View[], clauses: Clause[], dimensions: Dimension[]): Score { let map: { [key: string]: number } = {}; for (let i = 0; i < views.length; ++i) { @@ -165,6 +204,14 @@ export class ViewHandler { return map; } + /** + * Apply the reduce operation in a set of views. + * Returns the reduced views and the set of clauses which could + * not be applied. + * @param v - Set of views to be reduced. + * @param q - Target query. Used to know which dimentions ca be removed + * and which clauses must be applied. + */ private static applyReduce(v: View[], q: Query): ViewsAndClauses { let changed = true; const views = v.map((i) => i); @@ -256,6 +303,13 @@ export class ViewHandler { }; } + /** + * Finds the pair of views most realted in a set of views and + * applies the JOIN operation in this set. + * Returns a set with the joined view and without the most + * related view. + * @param v - Set of candidated views to be joined. + */ private static applyJoin(v: View[]): View[] { /* At this point 2 views will be joined, first the @@ -308,6 +362,12 @@ export class ViewHandler { return views; } + /** + * Calculates the similarity of two sets of dimensions. In other + * other how many dimensions are in both sets. + * @param a - A set of dimensions. + * @param b - Another set of dimensions. + */ private static similarDimensions(a: Dimension[], b: Dimension[]): number { let count = 0; for (let i = 0; i < a.length; ++i) { @@ -318,6 +378,10 @@ export class ViewHandler { return count; } + /** + * Removes repeated dimensions in a list of dimensions. + * @param candiateDims - A list of dimensions. + */ private static removeDuplicatedDimensions(candidateDims: Dimension[]): Dimension[] { let filterDims: { [key: string]: boolean } = {}; const dims = []; @@ -330,6 +394,10 @@ export class ViewHandler { return dims; } + /** + * Removes repeated dimensions in a list of metrics. + * @param candiateMets - A list of metrics. + */ private static removeDuplicatedMetrics(candidateMets: Metric[]): Metric[] { let filterMets: { [key: string]: boolean } = {}; const mets = []; @@ -342,6 +410,10 @@ export class ViewHandler { return mets; } + /** + * Removes repeated dimensions in a list of clauses. + * @param candiateClauses - A list of clauses. + */ private static removeDuplicatedClauses(candidateClauses: Clause[]): Clause[] { let filterClauses: { [key: string]: boolean } = {}; const clauses = []; diff --git a/yarn.lock b/yarn.lock index 947850a1e2a00c706cdd3e214a59f2a18f5386b5..96d8f20df54ea37887898695adfa575f4fa30f9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,14 +42,48 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" +"@types/fs-extra@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.1.tgz#cd856fbbdd6af2c11f26f8928fd8644c9e9616c9" + dependencies: + "@types/node" "*" + +"@types/glob@*": + version "5.0.35" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/handlebars@4.0.36": + version "4.0.36" + resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.36.tgz#ff57c77fa1ab6713bb446534ddc4d979707a3a79" + +"@types/highlight.js@9.12.2": + version "9.12.2" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.2.tgz#6ee7cd395effe5ec80b515d3ff1699068cd0cd1d" + "@types/js-yaml@^3.5.29": version "3.11.1" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.11.1.tgz#ac5bab26be5f9c6f74b6b23420f2cfa5a7a6ba40" +"@types/lodash@4.14.104": + version "4.14.104" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.104.tgz#53ee2357fa2e6e68379341d92eb2ecea4b11bb80" + +"@types/marked@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.3.0.tgz#583c223dd33385a1dda01aaf77b0cd0411c4b524" + "@types/mime@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" +"@types/minimatch@*", "@types/minimatch@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + "@types/mocha@^2.2.32": version "2.2.48" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab" @@ -79,6 +113,13 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/shelljs@0.7.8": + version "0.7.8" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.8.tgz#4b4d6ee7926e58d7bca448a50ba442fd9f6715bd" + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/superagent@*": version "3.5.8" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.5.8.tgz#da2b5d6a5bc77b4bf1b6d082ec4c3328ba5c3b9f" @@ -1086,6 +1127,14 @@ fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -1148,7 +1197,7 @@ glob@7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.0.5: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: @@ -1196,7 +1245,7 @@ got@~2.4.0: statuses "^1.2.1" timed-out "^2.0.0" -graceful-fs@^4.1.2: +graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1208,7 +1257,7 @@ growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" -handlebars@^4.0.3: +handlebars@^4.0.3, handlebars@^4.0.6: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: @@ -1240,6 +1289,10 @@ he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +highlight.js@^9.0.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" @@ -1320,6 +1373,10 @@ ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" +interpret@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + invariant@^2.1.0, invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -1565,6 +1622,12 @@ json3@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -1674,7 +1737,7 @@ lodash@^3.10.1: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.14.0, lodash@^4.17.4, lodash@^4.8.0: +lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.8.0: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" @@ -1712,7 +1775,7 @@ make-error@^1.1.1: version "1.3.4" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535" -marked@0.3.x: +marked@0.3.x, marked@^0.3.17: version "0.3.19" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" @@ -1776,7 +1839,7 @@ mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -2329,6 +2392,10 @@ process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + proxy-addr@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" @@ -2509,6 +2576,12 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" @@ -2556,7 +2629,7 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" -resolve@^1.1.7: +resolve@^1.1.6, resolve@^1.1.7: version "1.7.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" dependencies: @@ -2574,20 +2647,7 @@ rimraf@^2.6.1: dependencies: glob "^7.0.5" -"router@git+https://github.com/blakeembrey/router#router-engine": - version "1.1.4" - uid cfe18f9d59adca65c24c9c951ebd43e9bf07937e - resolved "git+https://github.com/blakeembrey/router#cfe18f9d59adca65c24c9c951ebd43e9bf07937e" - dependencies: - array-flatten "2.0.0" - debug "~2.2.0" - methods "~1.1.2" - parseurl "~1.3.1" - path-to-regexp "0.1.7" - setprototypeof "1.0.0" - utils-merge "1.0.0" - -"router@github:blakeembrey/router#router-engine": +router@blakeembrey/router#router-engine: version "1.1.4" resolved "https://codeload.github.com/blakeembrey/router/tar.gz/cfe18f9d59adca65c24c9c951ebd43e9bf07937e" dependencies: @@ -2670,6 +2730,14 @@ setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" +shelljs@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.2.tgz#345b7df7763f4c2340d584abb532c5f752ca9e35" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -2944,6 +3012,36 @@ type-is@^1.5.5, type-is@~1.6.15, type-is@~1.6.16: media-typer "0.3.0" mime-types "~2.1.18" +typedoc-default-themes@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz#6dc2433e78ed8bea8e887a3acde2f31785bd6227" + +typedoc@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.11.1.tgz#9f033887fd2218c769e1045feb88a1efed9f12c9" + dependencies: + "@types/fs-extra" "5.0.1" + "@types/handlebars" "4.0.36" + "@types/highlight.js" "9.12.2" + "@types/lodash" "4.14.104" + "@types/marked" "0.3.0" + "@types/minimatch" "3.0.3" + "@types/shelljs" "0.7.8" + fs-extra "^5.0.0" + handlebars "^4.0.6" + highlight.js "^9.0.0" + lodash "^4.17.5" + marked "^0.3.17" + minimatch "^3.0.0" + progress "^2.0.0" + shelljs "^0.8.1" + typedoc-default-themes "^0.5.0" + typescript "2.7.2" + +typescript@2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" + typescript@^2.4.1: version "2.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.3.tgz#5d817f9b6f31bb871835f4edf0089f21abe6c170" @@ -2980,6 +3078,10 @@ underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"