@quillsql/node 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const axios_1 = __importDefault(require("axios"));
16
+ /** This client is currently not used but is a good design pratice */
17
+ class QuillServerClient {
18
+ constructor(privateKey) {
19
+ this.baseUrl = "";
20
+ this.config = { headers: { Authorization: `Bearer ${privateKey}` } };
21
+ }
22
+ postQuill(route, payload) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ return axios_1.default.post(`${this.baseUrl}/${route}`, payload, this.config);
25
+ });
26
+ }
27
+ }
28
+ exports.default = QuillServerClient;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CachedPool = void 0;
13
+ const pg_1 = require("pg");
14
+ const redis_1 = require("redis");
15
+ const Error_1 = require("../utils/Error");
16
+ class PgError extends Error {
17
+ // Add other properties if needed
18
+ constructor(detail, hint, position) {
19
+ super();
20
+ this.detail = detail;
21
+ this.hint = hint;
22
+ this.position = position;
23
+ }
24
+ }
25
+ /** The TTL for new cache entries (default: 1h) */
26
+ const DEFAULT_CACHE_TTL = 24 * 60 * 60;
27
+ class CachedPool {
28
+ constructor(config, cacheConfig = {}) {
29
+ var _a;
30
+ this.pool = new pg_1.Pool(config);
31
+ this.ttl = (_a = cacheConfig === null || cacheConfig === void 0 ? void 0 : cacheConfig.ttl) !== null && _a !== void 0 ? _a : DEFAULT_CACHE_TTL;
32
+ this.cache = this.getCache(cacheConfig);
33
+ }
34
+ query(text, values) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ try {
37
+ if (!this.cache) {
38
+ const results = yield this.pool.query(text, values);
39
+ return {
40
+ fields: results.fields.map((field) => ({
41
+ name: field.name,
42
+ dataTypeID: field.dataTypeID,
43
+ })),
44
+ rows: results.rows,
45
+ };
46
+ }
47
+ const key = `${this.orgId}:${text}`;
48
+ const cachedResult = yield this.cache.get(key);
49
+ if (cachedResult) {
50
+ return JSON.parse(cachedResult);
51
+ }
52
+ else {
53
+ const newResult = yield this.pool.query(text, values);
54
+ const newResultString = JSON.stringify(newResult);
55
+ yield this.cache.set(key, newResultString, "EX", DEFAULT_CACHE_TTL);
56
+ return {
57
+ fields: newResult.fields.map((field) => ({
58
+ name: field.name,
59
+ dataTypeID: field.dataTypeID,
60
+ })),
61
+ rows: newResult.rows,
62
+ };
63
+ }
64
+ }
65
+ catch (err) {
66
+ if ((0, Error_1.isSuperset)(err, PgError)) {
67
+ throw new PgError(err.detail, err.hint, err.position);
68
+ }
69
+ else if (err instanceof Error) {
70
+ throw new Error(err.message);
71
+ }
72
+ }
73
+ });
74
+ }
75
+ /**
76
+ * Configures and returns a cache instance or null if none could be created.
77
+ */
78
+ getCache({ username, password, host, port, cacheType, }) {
79
+ if (cacheType === "redis" || cacheType === "rediss") {
80
+ const redisURL = `${cacheType}://${username}:${password}@${host}:${port}`;
81
+ return (0, redis_1.createClient)({ url: redisURL });
82
+ }
83
+ return null;
84
+ }
85
+ close() {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ yield this.pool.end();
88
+ });
89
+ }
90
+ }
91
+ exports.CachedPool = CachedPool;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const _1 = __importDefault(require("."));
16
+ require("dotenv/config");
17
+ const HOST = process.env.ENV === "development"
18
+ ? "http://localhost:8080"
19
+ : "https://quill-344421.uc.r.appspot.com";
20
+ describe("Quill", () => {
21
+ let quill;
22
+ beforeEach(() => {
23
+ quill = new _1.default(process.env.QUILL_PRIVATE_KEY, process.env.DB_URL);
24
+ });
25
+ afterAll(() => {
26
+ jest.restoreAllMocks();
27
+ });
28
+ describe("query", () => {
29
+ it("org - should return ", () => __awaiter(void 0, void 0, void 0, function* () {
30
+ const metadata = {
31
+ task: "orgs",
32
+ clientId: "62cda15d7c9fcca7bc0a3689",
33
+ };
34
+ const result = yield quill.query({
35
+ orgId: "2",
36
+ metadata,
37
+ });
38
+ // TODO - add assertions
39
+ }));
40
+ });
41
+ });
package/dist/index.js ADDED
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const CachedPools_1 = require("./db/CachedPools");
16
+ const axios_1 = __importDefault(require("axios"));
17
+ require("dotenv/config");
18
+ const RunQueryProcesses_1 = require("./utils/RunQueryProcesses");
19
+ const HOST = process.env.ENV === "development"
20
+ ? "http://localhost:8080"
21
+ : "https://quill-344421.uc.r.appspot.com";
22
+ /**
23
+ * Quill - Fullstack API Platform for Dashboards and Reporting.
24
+ */
25
+ class Quill {
26
+ constructor(privateKey, databaseConnectionString, cache = {}) {
27
+ this.ssl = { rejectUnauthorized: false };
28
+ this.baseUrl = HOST;
29
+ this.config = { headers: { Authorization: `Bearer ${privateKey}` } };
30
+ this.connectionString = databaseConnectionString;
31
+ this.ssl = { rejectUnauthorized: false };
32
+ this.targetPool = new CachedPools_1.CachedPool({ connectionString: this.connectionString, ssl: this.ssl }, cache);
33
+ }
34
+ query({ orgId, metadata }) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ this.targetPool.orgId = orgId;
37
+ try {
38
+ // Initial Query Request
39
+ const preQueryResults = yield this.runQueries(metadata.preQueries);
40
+ const response = yield this.postQuill(metadata.task, Object.assign(Object.assign({}, metadata), { orgId,
41
+ preQueryResults }));
42
+ const results = yield this.runQueries(response.queries, response.runQueryConfig);
43
+ // if there is no metadata object in the response, create one
44
+ if (!response.metadata) {
45
+ response.metadata = {};
46
+ }
47
+ // if there is a single query array in queryResults and the rows and field objects to metadata
48
+ if ((results === null || results === void 0 ? void 0 : results.queryResults.length) === 1) {
49
+ const queryResults = results.queryResults[0];
50
+ if (queryResults.rows) {
51
+ response.metadata.rows = queryResults.rows;
52
+ }
53
+ if (queryResults.fields) {
54
+ response.metadata.fields = queryResults.fields;
55
+ }
56
+ }
57
+ return {
58
+ data: response.metadata || null,
59
+ queries: results,
60
+ status: "success",
61
+ };
62
+ }
63
+ catch (err) {
64
+ return { status: "error", error: err.message };
65
+ }
66
+ });
67
+ }
68
+ runQueries(queries, runQueryConfig) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ let results;
71
+ if (!queries)
72
+ return Object.assign(Object.assign({}, results), { queryResults: [] });
73
+ if (runQueryConfig === null || runQueryConfig === void 0 ? void 0 : runQueryConfig.arrayToMap) {
74
+ return Object.assign(Object.assign({}, results), { mappedQueries: yield (0, RunQueryProcesses_1.mapQueries)(queries, runQueryConfig.arrayToMap, this.targetPool) });
75
+ }
76
+ else {
77
+ const queryResults = yield Promise.all(queries.map((query) => __awaiter(this, void 0, void 0, function* () {
78
+ return yield this.targetPool.query(query);
79
+ })));
80
+ results = Object.assign(Object.assign({}, results), { queryResults });
81
+ if (runQueryConfig === null || runQueryConfig === void 0 ? void 0 : runQueryConfig.getSchema) {
82
+ results = Object.assign(Object.assign({}, results), { columns: yield (0, RunQueryProcesses_1.getTableSchema)(queryResults[0], this.targetPool) });
83
+ }
84
+ if (runQueryConfig === null || runQueryConfig === void 0 ? void 0 : runQueryConfig.removeFields) {
85
+ results = Object.assign(Object.assign({}, results), { queryResults: (0, RunQueryProcesses_1.removeFields)(queryResults, runQueryConfig.removeFields) });
86
+ }
87
+ }
88
+ return results;
89
+ });
90
+ }
91
+ postQuill(path, payload) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ const response = yield axios_1.default.post(`${this.baseUrl}/sdk/${path}`, payload, this.config);
94
+ return response.data;
95
+ });
96
+ }
97
+ close() {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ yield this.targetPool.close();
100
+ });
101
+ }
102
+ }
103
+ exports.default = Quill;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const _1 = __importDefault(require("."));
7
+ jest.mock(".");
8
+ describe("Quill", () => {
9
+ let quill;
10
+ beforeEach(() => {
11
+ quill = new _1.default("dummy_private_key", "dummy_db_url");
12
+ quill.targetPool.query = jest.fn().mockResolvedValue([]);
13
+ });
14
+ describe("query", () => {
15
+ it("return nothing when suppling no queries", () => {
16
+ const metadata = {
17
+ task: "test",
18
+ queries: [],
19
+ };
20
+ const result = quill.query({
21
+ orgId: "dummy",
22
+ metadata,
23
+ });
24
+ expect(result).resolves.toEqual([]);
25
+ });
26
+ it("returns an error for the improper query", () => {
27
+ const metadata = {
28
+ task: "test",
29
+ queries: ["SELECT * FROM test"],
30
+ };
31
+ const result = quill.query({
32
+ orgId: "dummy",
33
+ metadata,
34
+ });
35
+ });
36
+ });
37
+ // Add more test cases as needed
38
+ });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSuperset = exports.PgError = void 0;
4
+ class PgError extends Error {
5
+ }
6
+ exports.PgError = PgError;
7
+ function isSuperset(obj, baseClass) {
8
+ // Get the property names of the base class
9
+ const baseProps = Object.getOwnPropertyNames(baseClass.prototype);
10
+ // Check if the object has all the properties of the base class
11
+ for (const prop of baseProps) {
12
+ if (!obj.hasOwnProperty(prop)) {
13
+ return false;
14
+ }
15
+ }
16
+ return true;
17
+ }
18
+ exports.isSuperset = isSuperset;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.mapQueries = exports.removeFields = exports.getTableSchema = void 0;
13
+ function getTableSchema(queryResults, targetPool) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ const typesQuery = yield targetPool.query("select typname, oid, typarray from pg_type order by oid;");
16
+ const schema = queryResults[0].fields.map((field) => {
17
+ return {
18
+ fieldType: typesQuery.rows.filter((type) => field.dataTypeID === type.oid)[0].typname,
19
+ name: field.name,
20
+ displayName: field.name,
21
+ isVisible: true,
22
+ };
23
+ });
24
+ return schema;
25
+ });
26
+ }
27
+ exports.getTableSchema = getTableSchema;
28
+ function removeFields(queryResults, fieldsToRemove) {
29
+ const fields = queryResults.fields.filter((field) => fieldsToRemove.includes(field.name));
30
+ const rows = queryResults.map((row) => {
31
+ fieldsToRemove.forEach((field) => {
32
+ delete row[field];
33
+ });
34
+ return { fields, rows };
35
+ });
36
+ }
37
+ exports.removeFields = removeFields;
38
+ function mapQueries(queries, arrayToMap, targetPool) {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ let arrayToMapResult = [];
41
+ for (let i = 0; i < queries.length; i++) {
42
+ const queryResult = yield targetPool.query(queries[i]);
43
+ arrayToMapResult.push(Object.assign(Object.assign({}, arrayToMap.arr[i]), { [arrayToMap.field]: queryResult.rows }));
44
+ }
45
+ return arrayToMapResult;
46
+ });
47
+ }
48
+ exports.mapQueries = mapQueries;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillsql/node",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Quill's client SDK for node backends.",
5
5
  "main": "dist/index.ts",
6
6
  "scripts": {