Skip to content

Commit

Permalink
First draft and implementation of MongooseDriver
Browse files Browse the repository at this point in the history
  • Loading branch information
nhat-phan committed Mar 22, 2018
1 parent d9a8361 commit 38361ac
Show file tree
Hide file tree
Showing 21 changed files with 388 additions and 27 deletions.
5 changes: 1 addition & 4 deletions dist/lib/drivers/DummyDriver.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { Eloquent } from '../model/Eloquent';
import { IEloquentDriver } from './interfaces/IEloquentDriver';
import { IBasicQuery } from '../query-builders/interfaces/IBasicQuery';
import { IConditionQuery } from '../query-builders/interfaces/IConditionQuery';
export declare class DummyDriver<T extends Object = {}> implements IEloquentDriver<T> {
static className: string;
attributes: Object;
model: Eloquent<T>;
initialize(data?: T): void;
getRecord(): T;
getAttribute(name: string): any;
setAttribute(name: string, value: any): boolean;
getId(): any;
setId(id: any): void;
newQuery(): IBasicQuery & IConditionQuery;
newQuery(): any;
toObject(): Object;
toJSON(): Object;
is(model: Eloquent<T>): boolean;
Expand Down
36 changes: 36 additions & 0 deletions dist/lib/drivers/MongooseDriver.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { IAutoload } from 'najs-binding';
import { Eloquent } from '../model/Eloquent';
import { EloquentMetadata } from '../model/EloquentMetadata';
import { IEloquentDriver } from './interfaces/IEloquentDriver';
import { Document, Model, Schema } from 'mongoose';
import { MongooseQueryBuilder } from '../query-builders/mongodb/MongooseQueryBuilder';
export declare class MongooseDriver<T extends Object = {}> implements IAutoload, IEloquentDriver {
attributes: Document & T;
metadata: EloquentMetadata;
mongooseModel: Model<Document & T>;
mongooseSchema: Schema;
queryLogGroup: string;
modelName: string;
constructor(model: Eloquent<T>);
getClassName(): string;
initialize(data?: T): void;
getRecord(): T;
getAttribute(name: string): any;
setAttribute(name: string, value: any): boolean;
getId(): any;
setId(id: any): void;
newQuery(): MongooseQueryBuilder<T>;
toObject(): Object;
toJSON(): Object;
is(model: Eloquent<T>): boolean;
formatAttributeName(name: string): string;
touch(): void;
save(): Promise<any>;
delete(): Promise<any>;
forceDelete(): Promise<any>;
restore(): Promise<any>;
fresh(): Promise<T | null>;
getReservedNames(): string[];
getDriverProxyMethods(): string[];
getQueryProxyMethods(): string[];
}
124 changes: 124 additions & 0 deletions dist/lib/drivers/MongooseDriver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = require("lodash");
const EloquentMetadata_1 = require("../model/EloquentMetadata");
const MongooseQueryBuilder_1 = require("../query-builders/mongodb/MongooseQueryBuilder");
class MongooseDriver {
constructor(model) {
this.metadata = EloquentMetadata_1.EloquentMetadata.get(model);
this.queryLogGroup = 'all';
}
getClassName() {
return 'NajsEloquent.MongooseProvider';
}
initialize(data) {
// this.attributes = data || {}
}
getRecord() {
return this.attributes;
}
getAttribute(name) {
return this.attributes[name];
}
setAttribute(name, value) {
this.attributes[name] = value;
return true;
}
getId() {
return this.attributes._id;
}
setId(id) {
this.attributes._id = id;
}
// TODO: implementation
newQuery() {
return new MongooseQueryBuilder_1.MongooseQueryBuilder(this.modelName, this.metadata.hasSoftDeletes() ? this.metadata.softDeletes() : undefined).setLogGroup(this.queryLogGroup);
}
// TODO: implementation
toObject() {
return this.attributes;
}
// TODO: implementation
toJSON() {
return this.attributes;
}
// TODO: implementation
is(model) {
return this.attributes['id'] === model['driver']['attributes']['id'];
}
formatAttributeName(name) {
return lodash_1.snakeCase(name);
}
touch() {
if (this.metadata.hasTimestamps()) {
const opts = this.metadata.timestamps();
this.attributes.markModified(opts.updatedAt);
}
}
async save() {
return this.attributes.save();
}
async delete() {
if (this.metadata.hasSoftDeletes()) {
return this.attributes['delete']();
}
return this.attributes.remove();
}
async forceDelete() {
return this.attributes.remove();
}
async restore() {
if (this.metadata.hasSoftDeletes()) {
return this.attributes['restore']();
}
}
async fresh() {
if (this.attributes.isNew) {
// tslint:disable-next-line
return null;
}
const query = this.newQuery();
return query.where(query.getPrimaryKey(), this.attributes._id).first();
}
getReservedNames() {
return ['schema', 'collection', 'schemaOptions'];
}
getDriverProxyMethods() {
return ['is', 'getId', 'setId', 'newQuery', 'touch', 'save', 'delete', 'forceDelete', 'restore', 'fresh'];
}
getQueryProxyMethods() {
return [
// IBasicQuery
'queryName',
'select',
'distinct',
'orderBy',
'orderByAsc',
'orderByDesc',
'limit',
// IConditionQuery
'where',
'orWhere',
'whereIn',
'whereNotIn',
'orWhereIn',
'orWhereNotIn',
'whereNull',
'whereNotNull',
'orWhereNull',
'orWhereNotNull',
// IFetchResultQuery
'get',
'all',
'find',
'first',
'count',
'pluck',
'update',
// 'delete', conflict to .getDriverProxyMethods() then it should be removed
// 'restore', conflict to .getDriverProxyMethods() then it should be removed
'execute'
];
}
}
exports.MongooseDriver = MongooseDriver;
5 changes: 3 additions & 2 deletions dist/lib/drivers/interfaces/IEloquentDriver.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Eloquent } from '../../model/Eloquent';
import { IBasicQuery } from '../../query-builders/interfaces/IBasicQuery';
import { IConditionQuery } from '../../query-builders/interfaces/IConditionQuery';
import { IFetchResultQuery } from '../../query-builders/interfaces/IFetchResultQuery';
export interface IEloquentDriverConstructor<Record extends Object = {}> {
constructor(model: Eloquent<Record>): any;
}
export interface IEloquentDriver<Record extends Object = {}> {
initialize(data?: Record): void;
initialize(data?: Record | Object): void;
getRecord(): Record;
getAttribute(name: string): any;
setAttribute(name: string, value: any): boolean;
getId(): any;
setId(id: any): void;
newQuery(): IBasicQuery & IConditionQuery;
newQuery(): IBasicQuery & IConditionQuery & IFetchResultQuery;
toObject(): Object;
toJSON(): Object;
is(model: Eloquent): boolean;
Expand Down
1 change: 1 addition & 0 deletions dist/lib/model/Eloquent.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export declare abstract class Eloquent<Record extends Object = {}> implements IA
constructor(data: Object);
constructor(data: Record);
abstract getClassName(): string;
getModelName(): string;
getAttribute(name: string): any;
setAttribute(name: string, value: any): boolean;
toObject(): Object;
Expand Down
3 changes: 3 additions & 0 deletions dist/lib/model/Eloquent.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class Eloquent {
return new Proxy(this, EloquentProxy_1.EloquentProxy);
}
}
getModelName() {
return this.getClassName();
}
getAttribute(name) {
return this.driver.getAttribute(name);
}
Expand Down
3 changes: 2 additions & 1 deletion dist/lib/query-builders/GenericQueryBuilder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export declare class GenericQueryBuilder implements IBasicQuery, IConditionQuery
protected getQueryConvention(): IQueryConvention;
protected getConditions(): Object[];
protected flattenFieldNames(type: string, fields: ArrayLike<any>): this;
setLogGroup(group: string): this;
queryName(name: string): this;
getPrimaryKey(): string;
setLogGroup(group: string): this;
select(field: string): this;
select(fields: string[]): this;
select(...fields: Array<string | string[]>): this;
Expand Down
11 changes: 7 additions & 4 deletions dist/lib/query-builders/GenericQueryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ class GenericQueryBuilder {
this.fields[type] = Array.from(new Set(lodash_1.flatten(fields))).map(this.convention.formatFieldName);
return this;
}
setLogGroup(group) {
this.logGroup = group;
return this;
}
queryName(name) {
this.name = name;
return this;
}
getPrimaryKey() {
return this.convention.formatFieldName('id');
}
setLogGroup(group) {
this.logGroup = group;
return this;
}
select() {
return this.flattenFieldNames('select', arguments);
}
Expand Down
1 change: 1 addition & 0 deletions dist/lib/query-builders/interfaces/IBasicQuery.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export declare type OrderDirection = 'asc' | 'desc';
export interface IBasicQuery {
queryName(name: string): this;
getPrimaryKey(): string;
setLogGroup(group: string): this;
select(field: string): this;
select(fields: string[]): this;
Expand Down
4 changes: 2 additions & 2 deletions dist/lib/query-builders/mongodb/MongooseQueryBuilder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { IFetchResultQuery } from '../interfaces/IFetchResultQuery';
import { Collection } from 'collect.js';
import { Model, Document, DocumentQuery } from 'mongoose';
export declare type MongooseQuery<T> = DocumentQuery<Document & T | null, Document & T> | DocumentQuery<(Document & T)[] | null, Document & T>;
export declare class MongooseQueryBuilder<T = {}> extends GenericQueryBuilder implements IBasicQuery, IFetchResultQuery<Document & T> {
export declare class MongooseQueryBuilder<T extends Object = {}> extends GenericQueryBuilder implements IBasicQuery, IFetchResultQuery<Document & T> {
static className: string;
protected mongooseModel: Model<Document & T>;
protected mongooseQuery: MongooseQuery<T>;
protected hasMongooseQuery: boolean;
protected primaryKey: string;
constructor(modelName: string);
constructor(modelName: string, softDelete: QueryBuilderSoftDelete);
constructor(modelName: string, softDelete: QueryBuilderSoftDelete | undefined);
constructor(modelName: string, softDelete: QueryBuilderSoftDelete | undefined, primaryKey: string);
protected getQuery(isFindOne?: boolean, logger?: MongooseQueryLog): MongooseQuery<T>;
protected passFieldsToQuery(query: MongooseQuery<T>, logger?: MongooseQueryLog): void;
Expand Down
6 changes: 6 additions & 0 deletions dist/test/model/Eloquent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ describe('Eloquent', function () {
createStub.restore();
});
});
describe('.getModelName()', function () {
it('returns .getClassName() by default', function () {
const model = new Model();
expect(model.getModelName()).toEqual(model.getClassName());
});
});
const forwardToDriverMethods = {
getAttribute: 'getAttribute',
setAttribute: 'setAttribute',
Expand Down
8 changes: 8 additions & 0 deletions dist/test/query-builders/GenericQueryBuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ const TestConvention = {
};
describe('QueryBuilder', function () {
describe('implements IBasicQuery', function () {
describe('.getPrimaryKey()', function () {
it('calls and returns convention.formatFieldName("id") by default', function () {
const query = new GenericQueryBuilder_1.GenericQueryBuilder();
const formatFieldNameSpy = Sinon.spy(query['convention'], 'formatFieldName');
expect(query.getPrimaryKey()).toEqual('id');
expect(formatFieldNameSpy.calledWith('id')).toBe(true);
});
});
describe('.queryName()', function () {
it('is chain-able, and simply sets provided value to "name"', function () {
const query = new GenericQueryBuilder_1.GenericQueryBuilder();
Expand Down
8 changes: 2 additions & 6 deletions lib/drivers/DummyDriver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { snakeCase } from 'lodash'
import { Eloquent } from '../model/Eloquent'
import { IEloquentDriver } from './interfaces/IEloquentDriver'
import { IBasicQuery } from '../query-builders/interfaces/IBasicQuery'
import { IConditionQuery } from '../query-builders/interfaces/IConditionQuery'

export class DummyDriver<T extends Object = {}> implements IEloquentDriver<T> {
static className: string = 'NajsEloquent.DummyDriver'

attributes: Object = {}
model: Eloquent<T>

initialize(data?: T): void {
this.attributes = data || {}
Expand All @@ -35,8 +31,8 @@ export class DummyDriver<T extends Object = {}> implements IEloquentDriver<T> {
this.setAttribute('id', id)
}

newQuery(): IBasicQuery & IConditionQuery {
return <any>{}
newQuery(): any {
return {}
}

toObject(): Object {
Expand Down
Loading

0 comments on commit 38361ac

Please sign in to comment.