Skip to content

Commit

Permalink
implement jest testing env (#4)
Browse files Browse the repository at this point in the history
* implement jest testing env

* handle auto-close property

* assign base adapter methods

* filter base methods before assigning them

* handle executing procedures

* update type declaration

* 2.9.0
  • Loading branch information
kbarbounakis authored Apr 29, 2024
1 parent 9ec301d commit 8009ad9
Show file tree
Hide file tree
Showing 11 changed files with 5,628 additions and 1,798 deletions.
24 changes: 9 additions & 15 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
{
"extends": [
"eslint:recommended",
"plugin:node/recommended"
"eslint:recommended"
],
"plugins": [
"node"
"node",
"jest"
],
"globals": {
"describe": false,
"it": false,
"fit": false,
"beforeEach": false,
"afterEach": false,
"beforeAll": false,
"afterAll": false,
"expect": false,
"jasmine": false
"env": {
"es6": true,
"node": true,
"jest/globals": true
},
"parserOptions": {
"ecmaVersion": 2020
"ecmaVersion": 2022
},
"rules": {
"node/exports-style": ["error", "module.exports"],
Expand All @@ -32,4 +26,4 @@
"node/prefer-promises/fs": "error",
"node/no-unpublished-require": "off"
}
}
}
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# test
spec
jest.config.js
jest.setup.js

# github
.github
Expand All @@ -15,4 +17,4 @@ spec
.idea

#gitpod
.gitpod.yml
.gitpod.yml
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// MOST Web Framework 2.0 Codename Blueshift Copyright (c) 2017-2020 THEMOST LP

import {ConfigurationBase} from '@themost/common';
import {AsyncEventEmitter} from '@themost/events';

declare type GenericPoolAdapterCallback = (err?: Error) => void;

export declare interface GenericPoolOptions {
adapter: string;
disableParallelTransactions?: boolean;
max?: number;
min?: number;
maxWaitingClients?: number;
Expand Down Expand Up @@ -36,6 +40,9 @@ export declare interface GenericPool {

export declare class GenericPoolAdapter {

static acquired: AsyncEventEmitter<{ name: string, target: GenericPool, borrowed: number, pending: number }>;
static released: AsyncEventEmitter<{ name: string, target: GenericPool, borrowed: number, pending: number }>;

static pool(name: string): GenericPool;
constructor(options: GenericPoolAdapterOptions);
open(callback: GenericPoolAdapterCallback): void;
Expand Down
128 changes: 80 additions & 48 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,15 @@ const { createPool } = require('generic-pool');
const { Args, TraceUtils } = require('@themost/common');
const { AsyncEventEmitter } = require('@themost/events');

const getConfigurationMethod = Symbol('getConfiguration');


class GenericPoolFactory {
/**
* @type {import('@themost/common').DataAdapterBase}
*/
_adapter = null;
/**
* @constructor
* @param {GenericPoolOptions=} options
*/
constructor(options) {
this.options = Object.assign({}, options);
this._adapter = null;
this.getConfiguration = null;
}

create() {
Expand All @@ -31,13 +26,13 @@ class GenericPoolFactory {
}

this.options = this.options || {};
if (typeof this[getConfigurationMethod] !== 'function') {
if (typeof this.getConfiguration !== 'function') {
throw new TypeError('Configuration getter must be a function.');
}
/**
* @type {import('@themost/common').ConfigurationBase}
*/
let configuration = this[getConfigurationMethod]();
let configuration = this.getConfiguration();
if (configuration == null) {
throw new TypeError('Configuration cannot be empty at this context.');
}
Expand Down Expand Up @@ -72,6 +67,10 @@ class GenericPoolFactory {

}

/**
* @param adapter
* @returns {Promise<void>}
*/
destroy(adapter) {
return new Promise((resolve, reject) => {
try {
Expand All @@ -97,6 +96,8 @@ class GenericPoolAdapter {
static pools = {};
static acquired = new AsyncEventEmitter();
static released = new AsyncEventEmitter();
executing = 0;
transaction = false;

/**
* @constructor
Expand All @@ -121,7 +122,11 @@ class GenericPoolAdapter {
* @param {Function} getConfigurationFunc
*/
hasConfiguration(getConfigurationFunc) {
this.pool._factory[getConfigurationMethod] = getConfigurationFunc;
/**
* @type {{_factory: *}}
*/
const { pool } = this;
pool._factory.getConfiguration = getConfigurationFunc;
}

/**
Expand All @@ -131,7 +136,7 @@ class GenericPoolAdapter {
open(callback) {
const self = this;
if (self.base) {
return self.base.open(callback);
return self.base.open((callback));
}
// get object from pool
self.pool.acquire().then(result => {
Expand All @@ -144,23 +149,22 @@ class GenericPoolAdapter {
});
// set base adapter
self.base = result;
//add lastIdentity() method by assigning base.lastIdentity
if (self.base && typeof self.base.lastIdentity === 'function') {
Object.assign(self, {
lastIdentity(callback) {
return this.base.lastIdentity(callback);
}
});
}
//add nextIdentity() method by assigning base.nextIdentity
if (self.base && typeof self.base.nextIdentity === 'function') {

// implement methods
// noinspection JSUnresolvedReference
const {
lastIdentity, lastIdentityAsync, nextIdentity, table, view, indexes
} = self.base;
[
lastIdentity, lastIdentityAsync, nextIdentity, table, view, indexes
].filter((func) => typeof func === 'function').filter((func) => {
return typeof self[func.name] === 'undefined';
}).forEach((func) => {
Object.assign(self, {
nextIdentity(entity, attribute, callback) {
return this.base.nextIdentity(entity, attribute, callback);
}
[func.name]: func.bind(self.base)
});
}
// assing extra property for current pool
});
// assign extra property for current pool
Object.defineProperty(self.base, 'pool', {
configurable: true,
enumerable: true,
Expand Down Expand Up @@ -211,14 +215,6 @@ class GenericPoolAdapter {
borrowed,
pending
});
// remove lastIdentity() method
if (typeof this.lastIdentity === 'function') {
delete this.lastIdentity;
}
// remove nextIdentity() method
if (typeof this.nextIdentity === 'function') {
delete this.nextIdentity;
}
// destroy local object
delete this.base;
}
Expand All @@ -241,12 +237,27 @@ class GenericPoolAdapter {
}

/**
* @param {function} callback
* @returns {void}
* @param {function(Error=)} callback
*/
tryClose(callback) {
// for future use
return callback();
// if executing procedures are more than 0
if (this.executing > 0) {
// decrease executing procedures
this.executing--;
}
// if transaction is active
if (this.transaction) {
// do nothing
// (do not try to close connection because transaction is active and will be closed later)
return callback();
}
// if executing procedures are more than 0
if (this.executing > 0) {
// do nothing (there are still executing procedures)
return callback();
}
// otherwise, auto-close connection
return this.close(callback);
}

/**
Expand All @@ -258,9 +269,17 @@ class GenericPoolAdapter {
execute(query, values, callback) {
const self = this;
try {
// increase executing procedures when a transaction is not active
// this operation is required to prevent connection auto-close during async operations
if (!self.transaction) {
self.executing++;
}
return self.open((err) => {
if (err) {
return callback(err);
// try to close connection to release connection immediately
self.tryClose(() => {
return callback(err);
});
}
return self.base.execute(query, values, (err, results) => {
// try close connection
Expand Down Expand Up @@ -294,7 +313,7 @@ class GenericPoolAdapter {

/**
* Executes an operation against database and returns the results.
* @param batch {*}
* @param _batch {*}
* @param callback {Function=}
*/
executeBatch(_batch, callback) {
Expand All @@ -321,7 +340,7 @@ class GenericPoolAdapter {
/**
* Creates a database view if the current data adapter supports views
* @param {string} name A string that represents the name of the view to be created
* @param {QueryExpression} query The query expression that represents the database vew
* @param {import('@themost/query').QueryExpression} query The query expression that represents the database vew
* @param {Function} callback A callback function to be called when operation will be completed.
*/
createView(name, query, callback) {
Expand Down Expand Up @@ -349,6 +368,15 @@ class GenericPoolAdapter {
if (err) {
return callback(err);
}
// validate transaction during async operation
if (self.transaction) {
if (self.options.disableParallelTransactions === true) {
return callback(new Error('Parallel transactions detected but this operation is not not allowed based on current configuration.'));
}
return executeFunc((err) => {
return callback(err);
});
}
self.transaction = true;
return self.base.executeInTransaction(executeFunc, (err) => {
self.transaction = false;
Expand Down Expand Up @@ -426,14 +454,15 @@ function createInstance(options) {
}
Args.check(name != null, 'Invalid argument. The target data adapter name is missing.');
/**
* @type {*}
* @type {{size:number=,timeout:number=}|*}
*/
const testOptions = options;
// upgrade from 2.2.x
if (typeof options.size === 'number') {
options.max = options.size
if (typeof testOptions.size === 'number') {
options.max = testOptions.size;
}
if (typeof options.timeout === 'number') {
options.acquireTimeoutMillis = options.timeout
if (typeof testOptions.timeout === 'number') {
options.acquireTimeoutMillis = testOptions.timeout;
}
let pool = GenericPoolAdapter.pools[name];
if (pool == null) {
Expand All @@ -449,7 +478,10 @@ function createInstance(options) {
GenericPoolAdapter.pools[name] = pool;
TraceUtils.debug(`GenericPoolAdapter: createPool() => name: ${name}, min: ${pool.min}, max: ${pool.max}`);
}
return new GenericPoolAdapter({ pool: name });
return new GenericPoolAdapter({
pool: name,
disableParallelTransactions: !!options.disableParallelTransactions,
});
}

process.on('exit', function () {
Expand All @@ -463,7 +495,7 @@ process.on('exit', function () {
return;
}
try {
TraceUtils.log(`GenericPoolAdapter: Cleaning up data pool ${key}`);
TraceUtils.info(`GenericPoolAdapter: Cleaning up data pool ${key}`);
const pool = GenericPoolAdapter.pools[key];
if (pool == null) {
return;
Expand Down
Loading

0 comments on commit 8009ad9

Please sign in to comment.