Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Allow to extend knex query builder #3334

Merged
merged 9 commits into from Jul 23, 2019
2 changes: 2 additions & 0 deletions src/knex.js
@@ -1,5 +1,6 @@
const Raw = require('./raw');
const Client = require('./client');
const QueryBuilder = require('./query/builder');

const makeKnex = require('./util/make-knex');
const parseConnection = require('./util/parse-connection');
Expand Down Expand Up @@ -56,6 +57,7 @@ function Knex(config) {

// Expose Client on the main Knex namespace.
Knex.Client = Client;
Knex.QueryBuilder = QueryBuilder;

/* eslint no-console:0 */

Expand Down
5 changes: 5 additions & 0 deletions src/query/builder.js
Expand Up @@ -43,6 +43,7 @@ function Builder(client) {
this._notFlag = false;
this._asColumnFlag = false;
}

inherits(Builder, EventEmitter);

const validateWithArgs = function(alias, statement, method) {
Expand Down Expand Up @@ -1219,4 +1220,8 @@ Builder.prototype.del = Builder.prototype.delete;
require('../interface')(Builder);
helpers.addQueryContext(Builder);

Builder.extend = function(methodName, fn) {
assign(Builder.prototype, { [methodName]: fn });
};

module.exports = Builder;
26 changes: 26 additions & 0 deletions test/unit/knex.js
Expand Up @@ -468,4 +468,30 @@ describe('knex', () => {
});
});
});

describe('extend query builder', () => {
let connection;
beforeEach(() => {
connection = new sqlite3.Database(':memory:');
});

afterEach(() => {
connection.close();
});

it('should extend default queryBuilder', (done) => {
Knex.QueryBuilder.extend('customSelect', function(value) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaks extended querybuilder outside of thus test, I think. Would be good to delete custom metode in after() block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you suggest to add remove method as well? cause with the current api, it always adds the method for the entire app.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't simple delete Knex.Querybuilder.prototype.customMethod work? I don't think we should expose deletion method via API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will work, I will update my PR soon

return this.select(this.client.raw(`${value} as value`));
});

const knex = Knex({ client: 'sqlite3' });
knex
.connection(connection)
.customSelect(42)
.then((result) => {
expect(result[0].value).to.equal(42);
done();
});
});
});
});
4 changes: 4 additions & 0 deletions types/index.d.ts
Expand Up @@ -353,6 +353,10 @@ interface Knex<TRecord extends {} = any, TResult = any[]>
seed: Knex.Seeder;
fn: Knex.FunctionHelper;
ref: Knex.RefBuilder;

QueryBuilder: {
extend(methodName: string, fn: Function): void;
};
}

declare function Knex<TRecord = any, TResult = unknown[]>(
Expand Down