Skip to content

Commit a4df3e3

Browse files
Implement extendable builders (knex#5041)
Co-authored-by: Olivier Cavadenti <[email protected]>
1 parent ad62d90 commit a4df3e3

File tree

11 files changed

+210
-1
lines changed

11 files changed

+210
-1
lines changed

lib/knex-builder/Knex.js

+28
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const QueryInterface = require('../query/method-constants');
55
const makeKnex = require('./make-knex');
66
const { KnexTimeoutError } = require('../util/timeout');
77
const { resolveConfig } = require('./internal/config-resolver');
8+
const SchemaBuilder = require('../schema/builder');
9+
const ViewBuilder = require('../schema/viewbuilder');
10+
const ColumnBuilder = require('../schema/columnbuilder');
11+
const TableBuilder = require('../schema/tablebuilder');
812

913
function knex(config) {
1014
const { resolvedConfig, Dialect } = resolveConfig(...arguments);
@@ -28,4 +32,28 @@ knex.QueryBuilder = {
2832
},
2933
};
3034

35+
knex.SchemaBuilder = {
36+
extend: function (methodName, fn) {
37+
SchemaBuilder.extend(methodName, fn);
38+
},
39+
};
40+
41+
knex.ViewBuilder = {
42+
extend: function (methodName, fn) {
43+
ViewBuilder.extend(methodName, fn);
44+
},
45+
};
46+
47+
knex.ColumnBuilder = {
48+
extend: function (methodName, fn) {
49+
ColumnBuilder.extend(methodName, fn);
50+
},
51+
};
52+
53+
knex.TableBuilder = {
54+
extend: function (methodName, fn) {
55+
TableBuilder.extend(methodName, fn);
56+
},
57+
};
58+
3159
module.exports = knex;

lib/migrations/migrate/MigrationGenerator.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class MigrationGenerator {
4141

4242
_getNewMigrationName(name) {
4343
if (name[0] === '-') name = name.slice(1);
44-
return yyyymmddhhmmss() + '_' + name + '.' + this.config.extension;
44+
return yyyymmddhhmmss() + '_' + name + '.' + this.config.extension.split('-')[0];
4545
}
4646

4747
_getNewMigrationPath(name) {
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @param { import("knex").Knex } knex
3+
* @returns { Promise<void> }
4+
*/
5+
exports.up = function({schema}) {
6+
<% if (d.tableName) { %>
7+
return schema.createTable("<%= d.tableName %>", function(t) {
8+
t.increments();
9+
t.timestamp();
10+
});
11+
<% } %>
12+
};
13+
14+
/**
15+
* @param { import("knex").Knex } knex
16+
* @returns { Promise<void> }
17+
*/
18+
exports.down = function({schema}) {
19+
<% if (d.tableName) { %>
20+
return schema.dropTable("<%= d.tableName %>");
21+
<% } %>
22+
};
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Knex } from "knex";
2+
3+
<% if (d.tableName) { %>
4+
export async function up({schema}: Knex): Promise<Knex.SchemaBuilder> {
5+
return schema.createTable("<%= d.tableName %>", (t) => {
6+
t.increments();
7+
t.timestamps();
8+
});
9+
}
10+
<% } else { %>
11+
export async function up({schema}: Knex): Promise<void> {
12+
}
13+
<% } %>
14+
<% if (d.tableName) { %>
15+
export async function down({schema}: Knex): Promise<Knex.SchemaBuilder> {
16+
return schema.dropTable("<%= d.tableName %>");
17+
}
18+
<% } else { %>
19+
export async function down({schema}: Knex): Promise<void> {
20+
}
21+
<% } %>

lib/schema/builder.js

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { EventEmitter } = require('events');
22
const toArray = require('lodash/toArray');
3+
const assign = require('lodash/assign');
34
const { addQueryContext } = require('../util/helpers');
45
const saveAsyncStack = require('../util/save-async-stack');
56
const {
@@ -96,6 +97,17 @@ class SchemaBuilder extends EventEmitter {
9697
};
9798
});
9899

100+
101+
SchemaBuilder.extend = (methodName, fn) => {
102+
if (Object.prototype.hasOwnProperty.call(SchemaBuilder.prototype, methodName)) {
103+
throw new Error(
104+
`Can't extend SchemaBuilder with existing method ('${methodName}').`
105+
);
106+
}
107+
108+
assign(SchemaBuilder.prototype, { [methodName]: fn });
109+
};
110+
99111
augmentWithBuilderInterface(SchemaBuilder);
100112
addQueryContext(SchemaBuilder);
101113

lib/schema/columnbuilder.js

+12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const extend = require('lodash/extend');
2+
const assign = require('lodash/assign');
23
const toArray = require('lodash/toArray');
34
const { addQueryContext } = require('../util/helpers');
45

@@ -87,6 +88,17 @@ ColumnBuilder.prototype.notNull = ColumnBuilder.prototype.notNullable =
8788
};
8889
});
8990

91+
92+
ColumnBuilder.extend = (methodName, fn) => {
93+
if (Object.prototype.hasOwnProperty.call(ColumnBuilder.prototype, methodName)) {
94+
throw new Error(
95+
`Can't extend ColumnBuilder with existing method ('${methodName}').`
96+
);
97+
}
98+
99+
assign(ColumnBuilder.prototype, { [methodName]: fn });
100+
};
101+
90102
const AlterMethods = {};
91103

92104
// Specify that the column is to be dropped. This takes precedence

lib/schema/tablebuilder.js

+12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// ------
99
const each = require('lodash/each');
1010
const extend = require('lodash/extend');
11+
const assign = require('lodash/assign');
1112
const toArray = require('lodash/toArray');
1213
const helpers = require('../util/helpers');
1314
const { isString, isFunction, isObject } = require('../util/is');
@@ -360,4 +361,15 @@ AlterMethods.dropColumn = AlterMethods.dropColumns = function () {
360361
return this;
361362
};
362363

364+
365+
TableBuilder.extend = (methodName, fn) => {
366+
if (Object.prototype.hasOwnProperty.call(TableBuilder.prototype, methodName)) {
367+
throw new Error(
368+
`Can't extend TableBuilder with existing method ('${methodName}').`
369+
);
370+
}
371+
372+
assign(TableBuilder.prototype, { [methodName]: fn });
373+
};
374+
363375
module.exports = TableBuilder;

lib/schema/viewbuilder.js

+12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const helpers = require('../util/helpers');
22
const extend = require('lodash/extend');
3+
const assign = require('lodash/assign');
34

45
class ViewBuilder {
56
constructor(client, method, viewName, fn) {
@@ -78,4 +79,15 @@ const AlterMethods = {
7879

7980
helpers.addQueryContext(ViewBuilder);
8081

82+
83+
ViewBuilder.extend = (methodName, fn) => {
84+
if (Object.prototype.hasOwnProperty.call(ViewBuilder.prototype, methodName)) {
85+
throw new Error(
86+
`Can't extend ViewBuilder with existing method ('${methodName}').`
87+
);
88+
}
89+
90+
assign(ViewBuilder.prototype, { [methodName]: fn });
91+
};
92+
8193
module.exports = ViewBuilder;

test/db-less-test-suite.js

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ describe('Query Building Tests', function () {
2020
require('./unit/query/string');
2121
require('./unit/schema-builder/mysql')('mysql');
2222
require('./unit/schema-builder/mysql')('mysql2');
23+
require('./unit/schema-builder/extensions');
2324
require('./unit/schema-builder/postgres');
2425
require('./unit/schema-builder/redshift');
2526
require('./unit/schema-builder/sqlite3');
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
const expect = require('chai').expect;
4+
const MySQL_Client = require('../../../lib/dialects/mysql');
5+
const knex = require('../../../knex');
6+
describe('SchemaBuilder Extensions', () => {
7+
const client = new MySQL_Client({ client: 'mysql' });
8+
9+
const equal = require('assert').equal;
10+
const functionToExtend = () => true;
11+
knex.SchemaBuilder.extend('tryCreateTable', functionToExtend);
12+
knex.TableBuilder.extend('testCol', functionToExtend);
13+
knex.ColumnBuilder.extend('testCol', functionToExtend);
14+
knex.ViewBuilder.extend('testCol', functionToExtend);
15+
16+
it('schemabuilder has method', () => {
17+
equal(client.schemaBuilder().tryCreateTable, functionToExtend);
18+
expect(client.schemaBuilder().tryCreateTable()).to.be.true;
19+
});
20+
21+
it('tablebuilder has method', () => {
22+
client
23+
.schemaBuilder()
24+
.createTable('users', function (table) {
25+
equal(table.testCol, functionToExtend);
26+
})
27+
.toSQL();
28+
});
29+
30+
it('columnbuilder has method', () => {
31+
client
32+
.schemaBuilder()
33+
.createTable('users', function (table) {
34+
equal(table.string('t').testCol, functionToExtend);
35+
})
36+
.toSQL();
37+
});
38+
39+
it('viewbuilder has method', () => {
40+
client
41+
.schemaBuilder()
42+
.createView('users', function (view) {
43+
equal(view.testCol, functionToExtend);
44+
view.as(
45+
knex('users')
46+
.join('user_roles', 'users.user_id', 'user_roles.user_id')
47+
.select('users.*', 'user_roles.role_code')
48+
);
49+
})
50+
.toSQL();
51+
});
52+
});

types/index.d.ts

+37
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,43 @@ export declare namespace knex {
384384
): void;
385385
}
386386

387+
class TableBuilder {
388+
static extend(
389+
methodName: string,
390+
fn: (
391+
this: Knex.TableBuilder,
392+
...args: any[]
393+
) => Knex.TableBuilder
394+
): void;
395+
}
396+
class ViewBuilder {
397+
static extend(
398+
methodName: string,
399+
fn: (
400+
this: Knex.ViewBuilder,
401+
...args: any[]
402+
) => Knex.ViewBuilder
403+
): void;
404+
}
405+
class SchemaBuilder {
406+
static extend(
407+
methodName: string,
408+
fn: (
409+
this: Knex.SchemaBuilder,
410+
...args: any[]
411+
) => Knex.SchemaBuilder
412+
): void;
413+
}
414+
class ColumnBuilder {
415+
static extend(
416+
methodName: string,
417+
fn: (
418+
this: Knex.ColumnBuilder,
419+
...args: any[]
420+
) => Knex.ColumnBuilder
421+
): void;
422+
}
423+
387424
export class KnexTimeoutError extends Error {}
388425

389426
export const Client: typeof Knex.Client;

0 commit comments

Comments
 (0)