diff --git a/config/ci_test.yaml.example b/config/ci_test.yaml.example index 7389688eb676dcfeb408f77badba2b485c76f2aa..2b810d85afcaae12cc5929427dc6a94410d06a0f 100644 --- a/config/ci_test.yaml.example +++ b/config/ci_test.yaml.example @@ -112,75 +112,120 @@ schema: name: "met:0" dataType: "integer" aggregation: "sum" + description: "No meaning, just used for test" - name: "met:1" dataType: "integer" aggregation: "avg" + description: "No meaning, just used for test" - name: "met:2" dataType: "integer" aggregation: "avg" + description: "No meaning, just used for test" - name: "met:3" dataType: "integer" aggregation: "sum" + description: "No meaning, just used for test" - name: "met:4" dataType: "integer" aggregation: "sum" + description: "No meaning, just used for test" - name: "met:5" dataType: "integer" aggregation: "avg" + description: "No meaning, just used for test" - name: "met:6" dataType: "integer" aggregation: "count" + description: "No meaning, just used for test" - name: "met:7" dataType: "integer" aggregation: "count" + description: "No meaning, just used for test" - name: "met:8" dataType: "integer" aggregation: "sum" + description: "No meaning, just used for test" - name: "met:9" dataType: "integer" aggregation: "count" + description: "No meaning, just used for test" - name: "met:10" dataType: "integer" aggregation: "max" + description: "No meaning, just used for test" - name: "met:11" dataType: "integer" aggregation: "min" + description: "No meaning, just used for test" dimensions: - name: "dim:0" dataType: "date" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:1" dataType: "date" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:2" dataType: "integer" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:3" dataType: "string" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:4" dataType: "string" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:5" dataType: "boolean" + description: "A dimension of Blendb. Has 2 possible values." - name: "dim:6" dataType: "integer" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:7" dataType: "integer" + description: "A dimension of Blendb. Has 5 possible values." - name: "dim:8" dataType: "integer" + description: "A dimension of Blendb. Has 5 possible values." + - + name: "dim:9" + dataType: "integer" + parent: "dim:0" + relation: "day" + description: "A dimension of Blendb. Has 30 possible values." + - + name: "dim:10" + dataType: "integer" + parent: "dim:0" + relation: "month" + description: "A dimension of Blendb. Has 12 possible values." + - + name: "dim:11" + dataType: "integer" + parent: "dim:0" + relation: "year" + description: "A dimension of Blendb. Has 1 possible value." + - + name: "dim:12" + dataType: "integer" + parent: "dim:0" + relation: "dayofweek" + description: "A dimension of Blendb. Has 7 possible values." diff --git a/src/api/controllers/data.spec.ts b/src/api/controllers/data.spec.ts index 429a0f27847bda2ce84ac29720c87f9610661cec..5cf78079cdf2b0e85ef8479b6b69e12dca38416f 100644 --- a/src/api/controllers/data.spec.ts +++ b/src/api/controllers/data.spec.ts @@ -70,7 +70,7 @@ describe("API data controller", () => { .expect((res: any) => { const message = "Query execution failed: " + "Could not construct query with the paramters given."; - const error = "The dimension named dim:11 was not found"; + const error = "The dimension named dim:-1 was not found"; expect(res.body).to.be.an("object"); expect(res.body).to.have.property("message"); expect(res.body).to.have.property("error"); diff --git a/src/api/controllers/engine.spec.ts b/src/api/controllers/engine.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc436712347eafbb1f57da91512ac50c7b5e141d --- /dev/null +++ b/src/api/controllers/engine.spec.ts @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blendb. + * + * blendb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blendb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blendb. If not, see <http://www.gnu.org/licenses/>. + */ + +import * as request from "supertest"; +import { expect } from "chai"; + +import * as server from "../../main"; + +describe("API engine controller", () => { + + it("should respond 200 and the list of metrics", (done) => { + request(server) + .get("/v1/metrics") + .expect(200) + .expect((res: any) => { + let result = res.body; + expect(result).to.be.an("array"); + expect(result).to.have.length(12); + }) + .end(done); + }); + + it("should respond 200 and the list of dimensions", (done) => { + request(server) + .get("/v1/dimensions") + .expect(200) + .expect((res: any) => { + let result = res.body; + expect(result).to.be.an("array"); + expect(result).to.have.length(13); + }) + .end(done); + }); + +}); diff --git a/src/api/controllers/engine.ts b/src/api/controllers/engine.ts new file mode 100644 index 0000000000000000000000000000000000000000..18de1e1a4252ecfed38bf340af6fe67a21b2f6fe --- /dev/null +++ b/src/api/controllers/engine.ts @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre + * Departamento de Informatica - Universidade Federal do Parana + * + * This file is part of blendb. + * + * blendb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * blendb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with blendb. If not, see <http://www.gnu.org/licenses/>. + */ + +import * as express from "express"; +import { Request } from "../types"; + +export class EngineCtrl { + public static metrics(req: Request, res: express.Response, next: express.NextFunction) { + res.status(200).json(req.engine.getMetricsDescription()); + } + + public static dimensions(req: Request, res: express.Response, next: express.NextFunction) { + res.status(200).json(req.engine.getDimensionsDescription()); + } +} diff --git a/src/api/router-v1.ts b/src/api/router-v1.ts index 1ac9ba57447635d537afc4507616cffd8ee3cbbc..53f3c05cf4998c2fa4fc90a30cee43fbef87b7b8 100644 --- a/src/api/router-v1.ts +++ b/src/api/router-v1.ts @@ -23,8 +23,11 @@ const osprey = require("osprey"); // import controllers import { DataCtrl } from "./controllers/data"; import { CollectCtrl } from "./controllers/collect"; +import { EngineCtrl } from "./controllers/engine"; export const router = osprey.Router(); +router.get("/metrics", EngineCtrl.metrics); +router.get("/dimensions", EngineCtrl.dimensions); router.get("/data", DataCtrl.read); router.post("/collect/{class}", CollectCtrl.write); diff --git a/src/core/dimension.ts b/src/core/dimension.ts index bf75265a66839a5dafc0385f0d74ca4c989c45b0..bda4e07fe43ad15b09e71294ebdd8abe4392746c 100644 --- a/src/core/dimension.ts +++ b/src/core/dimension.ts @@ -25,6 +25,15 @@ export interface DimensionOptions { dataType: string; parent?: Dimension; relation?: RelationType; + description?: string; +} + +export interface DimensionStrOptions { + name: string; + dataType: string; + parent?: string; + relation?: string; + description?: string; } export class Dimension { @@ -32,11 +41,63 @@ export class Dimension { public readonly dataType: string; public readonly parent: Dimension; public readonly relation: RelationType; + public readonly description: string; constructor(options: DimensionOptions) { this.name = options.name; this.dataType = options.dataType; this.relation = (options.relation) ? options.relation : RelationType.NONE; this.parent = (options.parent) ? options.parent : null; + this.description = (options.description) ? options.description : ""; + } + + public strOptions(): DimensionStrOptions { + if (this.relation === RelationType.NONE) { + return { + name: this.name, + dataType: this.dataType, + description: this.description + }; + } + + else { + return { + name: this.name, + dataType: this.dataType, + parent: this.parent.name, + relation: Dimension.stringifyRelationType(this.relation), + description: this.description + }; + } + } + + public static parseRelationType(r: string): RelationType { + switch (r) { + case "day": + return RelationType.DAY; + case "month": + return RelationType.MONTH; + case "year": + return RelationType.YEAR; + case "dayofweek": + return RelationType.DAYOFWEEK; + default: + return RelationType.NONE; + } + } + + public static stringifyRelationType(r: RelationType): string { + switch (r) { + case RelationType.DAY: + return "day"; + case RelationType.MONTH: + return "month"; + case RelationType.YEAR: + return "year"; + case RelationType.DAYOFWEEK: + return "dayofweek"; + default: + return ""; + } } } diff --git a/src/core/engine.spec.ts b/src/core/engine.spec.ts index 62f2f56a9fc3f66fbc2f9f0f523c39ad69a925a5..df5d4950421810f8c6bd46147a3778f11c48cf5a 100644 --- a/src/core/engine.spec.ts +++ b/src/core/engine.spec.ts @@ -55,7 +55,7 @@ describe("engine class", () => { expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(12); - expect(optimalView.dimensions).to.have.length(9); + expect(optimalView.dimensions).to.have.length(13); }); it("should throw an exception, query with non-existent metric", () => { let error: boolean = false; diff --git a/src/core/engine.ts b/src/core/engine.ts index 7305394454be9b384f5ed967ac3c95d1707b05d7..e1d842cfa473d950ed185a33b934bdb9600e911c 100644 --- a/src/core/engine.ts +++ b/src/core/engine.ts @@ -43,6 +43,14 @@ export class Engine { return this.views; } + public getMetricsDescription() { + return this.metrics.map((i) => i.strOptions()); + } + + public getDimensionsDescription() { + return this.dimensions.map((i) => i.strOptions()); + } + public addView(view: View) { if (this.graph.addView(view)) { this.views.push(view); diff --git a/src/core/metric.ts b/src/core/metric.ts index 4f0d1c4579d2fc9a83c97ea2cbbccd4545f15f28..7812a2c41e31edb592dfbc73750bbde03f30640a 100644 --- a/src/core/metric.ts +++ b/src/core/metric.ts @@ -24,16 +24,69 @@ export interface MetricOptions { name: string; aggregation: AggregationType; dataType: string; + description?: string; +} + +export interface MetricStrOptions { + name: string; + aggregation: string; + dataType: string; + description?: string; } export class Metric { public readonly name: string; public readonly aggregation: AggregationType; public readonly dataType: string; + public readonly description: string; constructor(options: MetricOptions) { this.name = options.name; this.aggregation = options.aggregation; this.dataType = options.dataType; + this.description = (options.description) ? options.description : ""; + } + + public strOptions(): MetricStrOptions { + return { + name: this.name, + aggregation: Metric.stringifyAggrType(this.aggregation), + dataType: this.dataType, + description: this.description + }; + } + + public static stringifyAggrType(a: AggregationType): string { + switch (a) { + case AggregationType.SUM: + return "sum"; + case AggregationType.AVG: + return "avg"; + case AggregationType.COUNT: + return "count"; + case AggregationType.MAX: + return "max"; + case AggregationType.MIN: + return "min"; + default: + return ""; + } + } + + public static parseAggrType (str: string): AggregationType { + switch (str) { + case "sum": + return AggregationType.SUM; + case "avg": + return AggregationType.AVG; + case "count": + return AggregationType.COUNT; + case "min": + return AggregationType.MIN; + case "max": + return AggregationType.MAX; + default: + return AggregationType.NONE; + } } } diff --git a/src/util/configParser.spec.ts b/src/util/configParser.spec.ts index 2b2a089904e95bc6dbffacc0f0d02a4176e4d737..973bad91619ac6d3f99fe340d36a7c598e1b072a 100644 --- a/src/util/configParser.spec.ts +++ b/src/util/configParser.spec.ts @@ -20,8 +20,8 @@ import { expect } from "chai"; -import { ConfigParser, ViewParsingOptions, DimensionStrOptions } from "./configParser"; -import { Dimension } from "../core/dimension"; +import { ConfigParser, ViewParsingOptions } from "./configParser"; +import { Dimension, DimensionStrOptions } from "../core/dimension"; import { RelationType } from "../common/types"; function strToRelationType (str: string): RelationType { diff --git a/src/util/configParser.ts b/src/util/configParser.ts index ea7133f9c20317439fe370f8fda7923e6ed5caba..938406fa26344a0ba400f4fabde4ae0731f3a8e8 100644 --- a/src/util/configParser.ts +++ b/src/util/configParser.ts @@ -18,10 +18,10 @@ * along with blendb. If not, see <http://www.gnu.org/licenses/>. */ -import { Metric, MetricOptions } from "../core/metric"; -import { Dimension, DimensionOptions } from "../core/dimension"; +import { Metric, MetricOptions, MetricStrOptions } from "../core/metric"; +import { Dimension, DimensionOptions, DimensionStrOptions } from "../core/dimension"; import { View, ViewOptions, LoadView } from "../core/view"; -import { AggregationType, RelationType } from "../common/types"; +import { RelationType } from "../common/types"; import { Filter } from "../core/filter"; import { Clause } from "../core/clause"; import { PoolConfig } from "pg"; @@ -39,19 +39,6 @@ export interface ViewParsingOptions { keys?: string[]; } -interface MetricStrOptions { - name: string; - aggregation: string; - dataType: string; -} - -export interface DimensionStrOptions { - name: string; - dataType: string; - parent?: string; - relation?: string; -} - interface ConfigSchema { views: ViewParsingOptions[]; metrics: MetricStrOptions[]; @@ -216,8 +203,9 @@ export class ConfigParser { return { name: opts.name, dataType: opts.dataType, + description: opts.description, parent: dims[i], - relation: this.strToRelationType(opts.relation) + relation: Dimension.parseRelationType(opts.relation) }; } } @@ -227,6 +215,7 @@ export class ConfigParser { return { name: opts.name, dataType: opts.dataType, + description: opts.description, parent: null, relation: RelationType.NONE }; @@ -235,8 +224,9 @@ export class ConfigParser { private static parseMetOpts (opts: MetricStrOptions): MetricOptions { return { name: opts.name, - aggregation: this.strToAggregationType(opts.aggregation), - dataType : opts.dataType + aggregation: Metric.parseAggrType(opts.aggregation), + dataType : opts.dataType, + description: opts.description }; } @@ -277,35 +267,4 @@ export class ConfigParser { }); } - private static strToAggregationType (str: string): AggregationType { - switch (str) { - case "sum": - return AggregationType.SUM; - case "avg": - return AggregationType.AVG; - case "count": - return AggregationType.COUNT; - case "min": - return AggregationType.MIN; - case "max": - return AggregationType.MAX; - default: - return AggregationType.NONE; - } - } - - private static strToRelationType (str: string): RelationType { - switch (str) { - case "day": - return RelationType.DAY; - case "month": - return RelationType.MONTH; - case "year": - return RelationType.YEAR; - case "dayofweek": - return RelationType.DAYOFWEEK; - default: - return RelationType.NONE; - } - } } diff --git a/test/scenario.ts b/test/scenario.ts index 347ff2bbf85961823972b46e0298d97d21d3612c..1fb2fac343ffd0ecc5596a7466ccfab1aa3ee0a1 100644 --- a/test/scenario.ts +++ b/test/scenario.ts @@ -153,7 +153,7 @@ const wrongMet = new Metric({ aggregation: AggregationType.COUNT, dataType: "integer" }); -const wrongDim = new Dimension({ name: "dim:11", dataType: "integer" }); +const wrongDim = new Dimension({ name: "dim:-1", dataType: "integer" }); const subdimAux = new Dimension({ name: "sub:0",