Skip to content

Commit

Permalink
feat: support disconnect and fix timestamps init (#313)
Browse files Browse the repository at this point in the history
* feat: close #309 support disconnect

* fix: close #311 Model.timestamps didn't init correct while use static attributes to define a model

Co-authored-by: JimmyDaddy <[email protected]>
  • Loading branch information
JimmyDaddy and JimmyDaddy authored May 13, 2022
1 parent a9a0eda commit 2ae1e31
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 10 deletions.
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,17 @@ const connect = async function connect(opts) {
return realm;
};

const disconnect = async function disconnect(realm, ...args) {
if (realm instanceof Realm && realm.connected) {
return await realm.disconnect(...args);
}
};

Object.assign(Realm.prototype, migrations, { DataTypes });
Object.assign(Realm, {
default: Realm,
connect,
disconnect,
Bone,
Collection,
DataTypes,
Expand Down
3 changes: 2 additions & 1 deletion src/bone.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
TIMESTAMP_NAMES,
LEGACY_TIMESTAMP_COLUMN_MAP,
ASSOCIATE_METADATA_MAP,
TIMESTAMP_ATTRIBUTE_NAMES,
} = require('./constants');

const columnAttributesKey = Symbol('leoric#columns');
Expand Down Expand Up @@ -999,7 +1000,7 @@ class Bone {
const attribute = new Attribute(name, attributes[name], options.define);
attributeMap[attribute.columnName] = attribute;
attributes[name] = attribute;
if (TIMESTAMP_NAMES.includes(name)) {
if (TIMESTAMP_ATTRIBUTE_NAMES.includes(name)) {
const { columnName } = attribute;
const legacyColumnName = LEGACY_TIMESTAMP_COLUMN_MAP[columnName];
if (!columnMap[columnName] && legacyColumnName && columnMap[legacyColumnName]) {
Expand Down
9 changes: 8 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ const LEGACY_TIMESTAMP_COLUMN_MAP = {
deleted_at: 'gmt_deleted',
};

const TIMESTAMP_ATTRIBUTE_NAMES = [
'createdAt', 'updatedAt', 'deletedAt',
'gmtCreate', 'gmtModified', 'gmtDeleted',
'created_at', 'updated_at', 'deleted_at',
'gmt_create', 'gmt_modified', 'gmt_deleted',
];
const TIMESTAMP_NAMES = [ 'createdAt', 'updatedAt', 'deletedAt' ];

const ASSOCIATE_METADATA_MAP = {
Expand All @@ -33,5 +39,6 @@ module.exports = {
LEGACY_TIMESTAMP_MAP,
TIMESTAMP_NAMES,
LEGACY_TIMESTAMP_COLUMN_MAP,
ASSOCIATE_METADATA_MAP
ASSOCIATE_METADATA_MAP,
TIMESTAMP_ATTRIBUTE_NAMES
};
9 changes: 9 additions & 0 deletions src/drivers/abstract/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const SqlString = require('sqlstring');
const debug = require('debug')('leoric');

const Logger = require('./logger');
const Attribute = require('./attribute');
Expand Down Expand Up @@ -54,6 +55,14 @@ class AbstractDriver {
throw new Error('unimplemented!');
}

/**
* disconnect manually
* @param {Function} callback
*/
async disconnect(callback) {
debug('[disconnect] called');
}

get dialect() {
return camelCase(this.constructor.name.replace('Driver', ''));
}
Expand Down
6 changes: 6 additions & 0 deletions src/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ class Realm {
return this.Bone;
}

async disconnect(callback) {
if (this.connected && this.driver) {
return await this.driver.disconnect(callback);
}
}

async sync(options) {
if (!this.connected) await this.connect();
const { models } = this;
Expand Down
31 changes: 29 additions & 2 deletions test/integration/custom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const path = require('path');
const sinon = require('sinon');
const SqlString = require('sqlstring');

const { connect, raw, Bone } = require('../..');
const { connect, raw, Bone, disconnect } = require('../..');
const { checkDefinitions } = require('./helpers');
const { formatConditions, collectLiteral } = require('../../src/expr_formatter');
const { findExpr } = require('../../src/expr');
Expand Down Expand Up @@ -170,10 +170,18 @@ class CustomDriver extends SqliteDriver {
const sql = `INSERT INTO ${table} (${valueSets.join(',')}) VALUES (${valueSets.map(_ => '?')})`;
return await this.query(sql, assignValues, options);
}

async disconnect(callback) {
// do nothing
callback && callback();
return true;
}
};

let realm;

before(async function() {
await connect({
realm = await connect({
driver: CustomDriver,
database: '/tmp/leoric.sqlite3',
models: path.resolve(__dirname, '../models'),
Expand Down Expand Up @@ -253,3 +261,22 @@ describe('=> upsert (sqlite)', function () {
);
});
});

describe('=> driver.disconnect', () => {
it('should be called', async () => {
let called = false;
const res = await realm.disconnect(() => called = true);
assert.equal(res, true);
assert.equal(called, true);
});

it('should be called with realm', async () => {
let called = false;
let res = await disconnect();
assert.ok(!res);
assert.equal(called, false);
res = await disconnect(realm, () => called = true);
assert.equal(res, true);
assert.equal(called, true);
});
});
108 changes: 108 additions & 0 deletions test/unit/timestamp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
'use strict';

const assert = require('assert').strict;
const { connect, Bone, DataTypes } = require('../..');

describe('connect', function() {
beforeEach(() => {
Bone.driver = null;
});

it('should work without define attributes clearly', async function() {
class Book extends Bone {}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
database: 'leoric',
Bone: Bone,
models: [ Book ],
});
assert.equal(Book.timestamps.createdAt, 'createdAt');
assert.equal(Book.attributes.createdAt.columnName, 'gmt_create');
assert.equal(Book.timestamps.updatedAt, 'updatedAt');
assert.equal(Book.attributes.updatedAt.columnName, 'gmt_modified');
assert.equal(Book.timestamps.deletedAt, 'deletedAt');
assert.equal(Book.attributes.deletedAt.columnName, 'gmt_deleted');

});

it('should work with define attributes clearly', async function() {
const { STRING, BIGINT, DECIMAL, DATE } = DataTypes;
class Book extends Bone {
static attributes = {
isbn: { type: BIGINT, primaryKey: true },
name: { type: STRING, allowNull: false },
price: { type: DECIMAL(10, 3), allowNull: false },
createdAt: { type: DATE },
updatedAt: { type: DATE },
deletedAt: { type: DATE },
}
}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
database: 'leoric',
models: [ Book ],
});
assert.equal(Book.timestamps.createdAt, 'createdAt');
assert.equal(Book.attributes.createdAt.columnName, 'gmt_create');
assert.equal(Book.timestamps.updatedAt, 'updatedAt');
assert.equal(Book.attributes.updatedAt.columnName, 'gmt_modified');
assert.equal(Book.timestamps.deletedAt, 'deletedAt');
assert.equal(Book.attributes.deletedAt.columnName, 'gmt_deleted');
});

it('should work with snake case', async function() {
const { STRING, BIGINT, DECIMAL, DATE } = DataTypes;
class Book extends Bone {
static attributes = {
isbn: { type: BIGINT, primaryKey: true },
name: { type: STRING, allowNull: false },
price: { type: DECIMAL(10, 3), allowNull: false },
created_at: { type: DATE },
updated_at: { type: DATE },
deleted_at: { type: DATE },
}
}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
database: 'leoric',
models: [ Book ],
});
assert.equal(Book.timestamps.createdAt, 'created_at');
assert.equal(Book.attributes.created_at.columnName, 'gmt_create');
assert.equal(Book.timestamps.updatedAt, 'updated_at');
assert.equal(Book.attributes.updated_at.columnName, 'gmt_modified');
assert.equal(Book.timestamps.deletedAt, 'deleted_at');
assert.equal(Book.attributes.deleted_at.columnName, 'gmt_deleted');
});


it('should work with mixed case', async function() {
const { STRING, BIGINT, DECIMAL, DATE } = DataTypes;
class Book extends Bone {
static attributes = {
isbn: { type: BIGINT, primaryKey: true },
name: { type: STRING, allowNull: false },
price: { type: DECIMAL(10, 3), allowNull: false },
created_at: { type: DATE },
updatedAt: { type: DATE },
deleted_at: { type: DATE },
}
}
await connect({
port: process.env.MYSQL_PORT,
user: 'root',
database: 'leoric',
models: [ Book ],
});
assert.equal(Book.timestamps.createdAt, 'created_at');
assert.equal(Book.attributes.created_at.columnName, 'gmt_create');
assert.equal(Book.timestamps.updatedAt, 'updatedAt');
assert.equal(Book.attributes.updatedAt.columnName, 'gmt_modified');
assert.equal(Book.timestamps.deletedAt, 'deleted_at');
assert.equal(Book.attributes.deleted_at.columnName, 'gmt_deleted');
});

});
20 changes: 14 additions & 6 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type DataTypes<T> = {
[Property in keyof T as Exclude<Property, "toSqlString">]: T[Property]
}

type RawQueryResult = typeof Bone | ResultSet | boolean | number;

interface ExprIdentifier {
type: 'id';
value: string;
Expand Down Expand Up @@ -314,6 +316,12 @@ declare class AbstractDriver {
* Grab a connection and query the database
*/
query(sql: string | { sql: string, nestTables?: boolean}, values?: Array<Literal | Literal[]>, opts?: SpellMeta): Promise<QueryResult>;

/**
* disconnect manually
* @param callback
*/
disconnect(callback?: Function): Promise<boolean | void>;

/**
* query with spell
Expand Down Expand Up @@ -686,8 +694,8 @@ export class Bone {
* yield Muscle.create({ boneId: bone.id, bar: 1 })
* });
*/
static transaction(callback: GeneratorFunction): Promise<void>;
static transaction(callback: (connection: Connection) => Promise<void>): Promise<void>;
static transaction(callback: GeneratorFunction): Promise<RawQueryResult>;
static transaction(callback: (connection: Connection) => Promise<RawQueryResult>): Promise<RawQueryResult>;

/**
* DROP the table
Expand Down Expand Up @@ -884,10 +892,10 @@ export default class Realm {

escape(value: Literal): string;

query(sql: string, values?: Array<Literal>, options?: RawQueryOptions): ResultSet;
query(sql: string, values?: Array<Literal>, options?: RawQueryOptions): RawQueryResult;

transaction(callback: GeneratorFunction): Promise<void>;
transaction(callback: (connection: Connection) => Promise<void>): Promise<void>;
transaction(callback: GeneratorFunction): Promise<RawQueryResult>;
transaction(callback: (connection: Connection) => Promise<RawQueryResult>): Promise<RawQueryResult>;

sync(options?: SyncOptions): Promise<void>;
}
Expand All @@ -903,7 +911,7 @@ export default class Realm {
* })
*/
export function connect(opts: ConnectOptions): Promise<Realm>;

export function disconnect(realm: Realm, callback?: Function): Promise<boolean | void>;
export {
Hint,
IndexHint,
Expand Down

0 comments on commit 2ae1e31

Please sign in to comment.