push column/idcolumn to connector level and renam
This commit is contained in:
parent
49e4c37ba7
commit
9d03b8cde8
|
@ -218,6 +218,62 @@ Connector.prototype.id = function(model, prop) {
|
||||||
return p && p.id;
|
return p && p.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the database name of the property of the model if it exists.
|
||||||
|
* Otherwise return the property name.
|
||||||
|
* Some connectors allow the column/field name to be customized
|
||||||
|
* at the model property definition level as `column`,
|
||||||
|
* `columnName`, or `field`. For example,
|
||||||
|
*
|
||||||
|
* ```json
|
||||||
|
* "name": {
|
||||||
|
* "type": "string",
|
||||||
|
* "mysql": {
|
||||||
|
* "column": "NAME"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @param {String} model The target model name
|
||||||
|
* @param {String} prop The property name
|
||||||
|
*
|
||||||
|
* @returns {String} The database mapping name of the property of the model if it exists
|
||||||
|
*/
|
||||||
|
Connector.prototype.getPropertyDbName = Connector.prototype.column =
|
||||||
|
function(model, property) {
|
||||||
|
const prop = this.getPropertyDefinition(model, property);
|
||||||
|
let mappingName;
|
||||||
|
if (prop && prop[this.name]) {
|
||||||
|
mappingName = prop[this.name].column || prop[this.name].columnName ||
|
||||||
|
prop[this.name].field || prop[this.name].fieldName;
|
||||||
|
if (mappingName) {
|
||||||
|
// Explicit column name, return as-is
|
||||||
|
return mappingName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if name attribute provided for column name
|
||||||
|
if (prop && prop.name) {
|
||||||
|
return prop.name;
|
||||||
|
}
|
||||||
|
mappingName = property;
|
||||||
|
if (typeof this.dbName === 'function') {
|
||||||
|
mappingName = this.dbName(mappingName);
|
||||||
|
}
|
||||||
|
return mappingName;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the database name of the id property of the model if it exists.
|
||||||
|
* Otherwise return the name of the id property.
|
||||||
|
* @param {String} model The target model name
|
||||||
|
* @param {String} prop The property name
|
||||||
|
* @returns {String} the database mapping name of the id property of the model if it exists.
|
||||||
|
*/
|
||||||
|
Connector.prototype.getIdDbName = Connector.prototype.idColumn = function(model) {
|
||||||
|
const idName = this.getDataSource(model).getModelDefinition(model).idName();
|
||||||
|
return this.getPropertyDbName(model, idName);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to be called by DataSource for defining a model
|
* Hook to be called by DataSource for defining a model
|
||||||
* @param {Object} modelDefinition The model definition
|
* @param {Object} modelDefinition The model definition
|
||||||
|
|
50
lib/sql.js
50
lib/sql.js
|
@ -354,46 +354,6 @@ SQLConnector.prototype.table = function(model) {
|
||||||
return tableName;
|
return tableName;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the column name for the given model property. The column name can be
|
|
||||||
* customized at the model property definition level as `column` or
|
|
||||||
* `columnName`. For example,
|
|
||||||
*
|
|
||||||
* ```json
|
|
||||||
* "name": {
|
|
||||||
* "type": "string",
|
|
||||||
* "mysql": {
|
|
||||||
* "column": "NAME"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param {String} model The model name
|
|
||||||
* @param {String} property The property name
|
|
||||||
* @returns {String} The column name
|
|
||||||
*/
|
|
||||||
SQLConnector.prototype.column = function(model, property) {
|
|
||||||
const prop = this.getPropertyDefinition(model, property);
|
|
||||||
let columnName;
|
|
||||||
if (prop && prop[this.name]) {
|
|
||||||
columnName = prop[this.name].column || prop[this.name].columnName;
|
|
||||||
if (columnName) {
|
|
||||||
// Explicit column name, return as-is
|
|
||||||
return columnName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if name attribute provided for column name
|
|
||||||
if (prop && prop.name) {
|
|
||||||
return prop.name;
|
|
||||||
}
|
|
||||||
columnName = property;
|
|
||||||
if (typeof this.dbName === 'function') {
|
|
||||||
columnName = this.dbName(columnName);
|
|
||||||
}
|
|
||||||
return columnName;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the column metadata for the given model property
|
* Get the column metadata for the given model property
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
|
@ -420,16 +380,6 @@ SQLConnector.prototype.propertyName = function(model, column) {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the id column name.
|
|
||||||
* @param {String} model The model name
|
|
||||||
* @returns {String} The id column name
|
|
||||||
*/
|
|
||||||
SQLConnector.prototype.idColumn = function(model) {
|
|
||||||
const idName = this.getDataSource(model).getModelDefinition(model).idName();
|
|
||||||
return this.column(model, idName);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the escaped id column name
|
* Get the escaped id column name
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright IBM Corp. 2020. All Rights Reserved.
|
||||||
|
// Node module: loopback-connector
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
/*
|
||||||
|
* A mockup connector that extends NoSQL/SQL connector
|
||||||
|
* to check property name mapping.
|
||||||
|
*/
|
||||||
|
const util = require('util');
|
||||||
|
const Connector = require('../../lib/connector');
|
||||||
|
const debug = require('debug')('loopback:connector:test-connector');
|
||||||
|
|
||||||
|
exports.initialize = function initializeDataSource(dataSource, callback) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
if (callback) {
|
||||||
|
const connector = new TestConnector(dataSource.settings);
|
||||||
|
connector.dataSource = dataSource;
|
||||||
|
dataSource.connector = connector;
|
||||||
|
callback(null, connector);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function TestConnector(settings) {
|
||||||
|
Connector.call(this, 'testdb', settings);
|
||||||
|
this._tables = {};
|
||||||
|
this.data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(TestConnector, Connector);
|
||||||
|
|
||||||
|
TestConnector.prototype.dbName = function(name) {
|
||||||
|
return name.toUpperCase();
|
||||||
|
};
|
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright IBM Corp. 2020. All Rights Reserved.
|
||||||
|
// Node module: loopback-connector
|
||||||
|
// This file is licensed under the MIT License.
|
||||||
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const expect = require('chai').expect;
|
||||||
|
const Connector = require('../lib/connector');
|
||||||
|
const testConnector = require('./connectors/test-connector');
|
||||||
|
|
||||||
|
const juggler = require('loopback-datasource-juggler');
|
||||||
|
const ds = new juggler.DataSource({
|
||||||
|
connector: testConnector,
|
||||||
|
debug: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-disable one-var */
|
||||||
|
let connector;
|
||||||
|
let Customer;
|
||||||
|
let Order;
|
||||||
|
/* eslint-enable one-var */
|
||||||
|
|
||||||
|
describe('Name mapping', function() {
|
||||||
|
let connector, builder;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
connector = ds.connector;
|
||||||
|
connector._tables = {};
|
||||||
|
connector._models = {};
|
||||||
|
Customer = ds.createModel(
|
||||||
|
'Customer',
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
id: true,
|
||||||
|
type: String,
|
||||||
|
testdb: {
|
||||||
|
column: 'FIRSTNAME',
|
||||||
|
dataType: 'VARCHAR',
|
||||||
|
dataLength: 32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
middleName: {
|
||||||
|
type: Boolean,
|
||||||
|
name: 'middle_name',
|
||||||
|
postgresql: {
|
||||||
|
column: 'MIDDLENAME',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
type: Boolean,
|
||||||
|
testdb: {
|
||||||
|
column: 'LASTNAME',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
primaryAddress: {
|
||||||
|
type: String,
|
||||||
|
name: 'primary_address',
|
||||||
|
},
|
||||||
|
address: String,
|
||||||
|
},
|
||||||
|
{testdb: {table: 'CUSTOMER'}},
|
||||||
|
);
|
||||||
|
|
||||||
|
// use field in this model to mock NoSQL DB
|
||||||
|
Order = ds.createModel(
|
||||||
|
'Order',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
id: true,
|
||||||
|
type: Number,
|
||||||
|
testdb: {
|
||||||
|
field: 'my_id',
|
||||||
|
dataType: 'ObjectId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
des: {
|
||||||
|
type: String,
|
||||||
|
testdb: {
|
||||||
|
field: 'DESCRIPTION',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{testdb: {table: 'ORDER'}},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
context('getIdDbName', function() {
|
||||||
|
it('should map id column name', function() {
|
||||||
|
const idCol = connector.getIdDbName('Customer');
|
||||||
|
expect(idCol).to.eql('FIRSTNAME');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('alias idColumn should map id column name', function() {
|
||||||
|
const idCol = connector.idColumn('Customer');
|
||||||
|
expect(idCol).to.eql('FIRSTNAME');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should map id field name', function() {
|
||||||
|
const idCol = connector.getIdDbName('Order');
|
||||||
|
expect(idCol).to.eql('my_id');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
context('getPropertyDbName', function() {
|
||||||
|
it('prefers property name if the database name is not matched', function() {
|
||||||
|
const column = connector.getPropertyDbName('Customer', 'middleName');
|
||||||
|
expect(column).to.eql('middle_name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefers database-specific column name over property name', function() {
|
||||||
|
const column = connector.getPropertyDbName('Customer', 'lastName');
|
||||||
|
expect(column).to.eql('LASTNAME');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('alias column should map the column name', function() {
|
||||||
|
const column = connector.column('Customer', 'lastName');
|
||||||
|
expect(column).to.eql('LASTNAME');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('propertyMapping should map column name from name attribute', function() {
|
||||||
|
const column = connector.getPropertyDbName('Customer', 'primaryAddress');
|
||||||
|
expect(column).to.eql('primary_address');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('connector-preffered configuration (UPPERCASE) is applied if no columm/field name is provided', function() {
|
||||||
|
const column = connector.getPropertyDbName('Customer', 'address');
|
||||||
|
expect(column).to.eql('ADDRESS');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefers database-specific field name over property name', function() {
|
||||||
|
const column = connector.getPropertyDbName('Order', 'des');
|
||||||
|
expect(column).to.eql('DESCRIPTION');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -79,37 +79,13 @@ describe('sql connector', function() {
|
||||||
{testdb: {table: 'ORDER'}});
|
{testdb: {table: 'ORDER'}});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// tests for column names mapping are moved to name-mapping.test.js
|
||||||
|
|
||||||
it('should map table name', function() {
|
it('should map table name', function() {
|
||||||
const table = connector.table('customer');
|
const table = connector.table('customer');
|
||||||
expect(table).to.eql('CUSTOMER');
|
expect(table).to.eql('CUSTOMER');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should map column name', function() {
|
|
||||||
const column = connector.column('customer', 'name');
|
|
||||||
expect(column).to.eql('NAME');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should map column name from name attribute', function() {
|
|
||||||
const column = connector.column('customer', 'primaryAddress');
|
|
||||||
expect(column).to.eql('primary_address');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('prefers database-specific column name over property name', function() {
|
|
||||||
const column = connector.column('customer', 'lastName');
|
|
||||||
expect(column).to.eql('LASTNAME');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses database-specific column name over property name even if the column name \
|
|
||||||
does not follow the connector-specific configuration (UPPERCASE)', function() {
|
|
||||||
const column = connector.column('order', 'des');
|
|
||||||
expect(column).to.eql('description');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('prefers property name when database is different', function() {
|
|
||||||
const column = connector.column('customer', 'middleName');
|
|
||||||
expect(column).to.eql('middle_name');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find column metadata', function() {
|
it('should find column metadata', function() {
|
||||||
const column = connector.columnMetadata('customer', 'name');
|
const column = connector.columnMetadata('customer', 'name');
|
||||||
expect(column).to.eql({
|
expect(column).to.eql({
|
||||||
|
@ -124,18 +100,6 @@ describe('sql connector', function() {
|
||||||
expect(prop).to.eql('name');
|
expect(prop).to.eql('name');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should map id column name', function() {
|
|
||||||
const idCol = connector.idColumn('customer');
|
|
||||||
expect(idCol).to.eql('NAME');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should map id column name even if the column name does not \
|
|
||||||
follow the connector-specific configuration (UPPERCASE)', function() {
|
|
||||||
const idCol = connector.idColumn('order');
|
|
||||||
// shouldn't be converted to ORDERID
|
|
||||||
expect(idCol).to.eql('orderId');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find escaped id column name', function() {
|
it('should find escaped id column name', function() {
|
||||||
const idCol = connector.idColumnEscaped('customer');
|
const idCol = connector.idColumnEscaped('customer');
|
||||||
expect(idCol).to.eql('`NAME`');
|
expect(idCol).to.eql('`NAME`');
|
||||||
|
|
Loading…
Reference in New Issue