Skip to content

Commit

Permalink
Merge pull request #13 from acalvoa/add_native_query_support
Browse files Browse the repository at this point in the history
Feature: Add native query support
  • Loading branch information
acalvoa authored Sep 30, 2019
2 parents 12a3466 + 8dc504c commit d479e19
Show file tree
Hide file tree
Showing 15 changed files with 423 additions and 29 deletions.
31 changes: 27 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,35 @@ afterDestroy(newRecord, proceed, req);

```

## 6. Examples
## 6. Native Queries

The hook support native queries in each adapters that can support sendNativeQuery Method.

To use native query method in the datastore, we can use the same ways used in the ORM methods to interact with database using the three posible ways to handle multitenancy shown in this hook.

To use native queries you can use this ways, if you want use request object.


```javascript
let QUERY = "SELECT * FROM test_table";

// Using native queries from sails object, using request object
let query = await sails.sendNativeQuery(req, QUERY);

// Using native queries from model, using request object
// Test is a declared Sails Model.
let query = Test.getDatastore().sendNativeQuery(req, QUERY);

```

If you want to use Datasource creation or Configuration object Way, you can use them like a Multi Tenancy model action, replacing request object with the with the corresponding variables.

## 7. Examples
An example project for study is in the example folder.

If you have any question of how to use, or any question, please contact.

## 7. Tests
## 8. Tests
Follow the Sails documentation, the hook is tested with mocha.

```bash
Expand Down Expand Up @@ -431,7 +454,7 @@ Git: https://www.github.com/acalvoa/sails-hook-multitenant
```


## 8. Contributors
## 9. Contributors
Thanks to all people that can do this possible.
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
Expand All @@ -440,7 +463,7 @@ Thanks to all people that can do this possible.

**Knownledge is power, share the Knownledge.**

## 9. License
## 10. License
This project is develop by Parley for free use by the community, under MIT license.

Made with ❤ in Chile
54 changes: 45 additions & 9 deletions lib/initialize.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
var schemas = require('./methods/getschemas');
var multitenant = require('./methods/multitenant');
var addDatasource = require('./methods/adddatasource');
var addDatasourceAdapter = require('./methods/add-adapter-datasource');
var d = require('./methods/datasource');
var actions = require('./internal/methods');
const sendNativeQuery = require('./internal/helpers/send-native-query');
const getTenancyDatastore = require('./methods/get-tenancy-datastore');

module.exports = function inicialize(sails, cb) {
console.log
(`
console.log(`
==========================================================
_____ _____ _ _ _____ __ __ _____
| ___| | _ | | | | | | ___| | \\/ | |_ _|
| |___ | |_| | | | | | | |___ | | | |
|____ | | _ | | | | | |____ | | |\\/| | | |
___| | | | | | | | | |__ ___| | | | | | | |
|_____| |_| |_| |_| |____||_____| |_| |_| |_|
███████╗ █████╗ ██╗██╗ ███████╗ ███╗ ███╗████████╗
██╔════╝██╔══██╗██║██║ ██╔════╝ ████╗ ████║╚══██╔══╝
███████╗███████║██║██║ ███████╗ ██╔████╔██║ ██║
╚════██║██╔══██║██║██║ ╚════██║ ██║╚██╔╝██║ ██║
███████║██║ ██║██║███████╗███████║ ██║ ╚═╝ ██║ ██║
╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝
Waterline Multitenant ORM Project
License: MIT
Expand All @@ -37,10 +40,18 @@ If you use the Request Object way, please define a multitenancy function in a co
================================================================================`)
};
// Alter the schemas to add Multitenancy properties

// ███╗ ███╗ ██████╗ ██████╗ ███████╗██╗ ███████╗
// ████╗ ████║██╔═══██╗██╔══██╗██╔════╝██║ ██╔════╝
// ██╔████╔██║██║ ██║██║ ██║█████╗ ██║ ███████╗
// ██║╚██╔╝██║██║ ██║██║ ██║██╔══╝ ██║ ╚════██║
// ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████╗███████╗███████║
// ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝

// Alter the schemas to add Multitenancy properties
for (let key in sails.hooks.orm.models) {
let Model = sails.hooks.orm.models[key];

// Add the tenancy function to make posible the multitenancy select
Model.tenancy = async function(req, ignore_tenancy, ParentModel) {
const _self = this;
Expand Down Expand Up @@ -81,6 +92,31 @@ If you use the Request Object way, please define a multitenancy function in a co
Model[key] = actions[key];
}
}

// ███╗ ██╗ █████╗ ████████╗██╗██╗ ██╗███████╗ ██████╗ ██╗ ██╗███████╗██████╗ ██╗ ██╗
// ████╗ ██║██╔══██╗╚══██╔══╝██║██║ ██║██╔════╝ ██╔═══██╗██║ ██║██╔════╝██╔══██╗╚██╗ ██╔╝
// ██╔██╗ ██║███████║ ██║ ██║██║ ██║█████╗ ██║ ██║██║ ██║█████╗ ██████╔╝ ╚████╔╝
// ██║╚██╗██║██╔══██║ ██║ ██║╚██╗ ██╔╝██╔══╝ ██║▄▄ ██║██║ ██║██╔══╝ ██╔══██╗ ╚██╔╝
// ██║ ╚████║██║ ██║ ██║ ██║ ╚████╔╝ ███████╗ ╚██████╔╝╚██████╔╝███████╗██║ ██║ ██║
// ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝

sails.sendNativeQuery = sendNativeQuery.bind(sails.getDatastore());
// Add the tenancy function to make posible the multitenancy select
sails.nativeTenancy = async function(req, ignore_tenancy, datasource) {
// If the model not have a multitenant return self;
if (ignore_tenancy) {
return datasource;
}
// If diferent go to search the multitentant;
return await getTenancyDatastore(req, datasource, sails);
}
// Add addDatasource
sails.addDatasource = async function(identity, config){
const _self = this;
if(config.constructor.name === 'DataStoreConfig'){
return await addDatasourceAdapter(sails, SchemaMap, config, identity);
}
}
})
// Do some stuff here to initialize hooks
// And then call `cb` to continue
Expand Down
146 changes: 146 additions & 0 deletions lib/internal/helpers/send-native-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
var _ = require('@sailshq/lodash');
var parley = require('parley');
var flaverr = require('flaverr');
var _datasources = require('../../methods/getdatasources');
var helpSendNativeQuery = require('sails-hook-orm/lib/datastore-method-utils/help-send-native-query');
var adapterSupportVerify = require('../../methods/adapter-support-verify');

module.exports = function(req, _nativeQuery, _valuesToEscape, explicitCb, _meta, more) {

// ███╗ ███╗██╗ ██╗██╗ ████████╗██╗████████╗███████╗███╗ ██╗ █████╗ ███╗ ██╗ ██████╗██╗ ██╗
// ████╗ ████║██║ ██║██║ ╚══██╔══╝██║╚══██╔══╝██╔════╝████╗ ██║██╔══██╗████╗ ██║██╔════╝╚██╗ ██╔╝
// ██╔████╔██║██║ ██║██║ ██║ ██║ ██║ █████╗ ██╔██╗ ██║███████║██╔██╗ ██║██║ ╚████╔╝
// ██║╚██╔╝██║██║ ██║██║ ██║ ██║ ██║ ██╔══╝ ██║╚██╗██║██╔══██║██║╚██╗██║██║ ╚██╔╝
// ██║ ╚═╝ ██║╚██████╔╝███████╗██║ ██║ ██║ ███████╗██║ ╚████║██║ ██║██║ ╚████║╚██████╗ ██║
// ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝

const _self = this;
_self.datastore = _self.s_datastore;
var ignore_tenancy = true;
// Get datasource
const datasources = _datasources(null, true);
// Define the args array to get the tenancy var
var args = Array.from(arguments);
// Tenancy req argument analisis
if ((typeof args[0] === 'string' && typeof args[1] === 'string')
|| typeof args[0] === 'object') {
if(typeof args[0] === 'string' && datasources.searchStringIdentity(args[0])) {
ignore_tenancy = false;
} else if (typeof args[0] === 'object' && (args[0].constructor.name === 'IncomingMessage' || args[0].constructor.name === 'DataStoreConfig')) {
ignore_tenancy = false;
} else {
_nativeQuery = args[0];
_valuesToEscape = args[1];
explicitCb = args[2];
_meta = args[3];
more = args[4]
}
} else {
_nativeQuery = args[0];
_valuesToEscape = args[1];
explicitCb = args[2];
_meta = args[3];
more = args[4]
}

// Handle variadic usage:
// ```
// sendNativeQuery('foo', function(){...})
// ```

if (arguments.length === 2 && _.isFunction(_valuesToEscape)) {
explicitCb = _valuesToEscape;
_valuesToEscape = undefined;
}
// ```
// sendNativeQuery('foo', function(){...}, ()=>{…})
// ```
else if (arguments.length === 3 && _.isFunction(_valuesToEscape)) {
_meta = explicitCb;
explicitCb = _valuesToEscape;
_valuesToEscape = undefined;
}

return parley(function _handleExec(done){

// ████████╗ ███████╗███████╗██╗ ███████╗ ██████╗████████╗ ██████╗ ██████╗
// ╚══██╔══╝ ██╔════╝██╔════╝██║ ██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗
// ██║ ███████╗█████╗ ██║ █████╗ ██║ ██║ ██║ ██║██████╔╝
// ██║ ╚════██║██╔══╝ ██║ ██╔══╝ ██║ ██║ ██║ ██║██╔══██╗
// ██║██╗ ███████║███████╗███████╗███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║
// ╚═╝╚═╝ ╚══════╝╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝

sails.nativeTenancy(req, ignore_tenancy, _self).then(datastore => {

const adapter = sails.hooks.orm.adapters[datastore.config.adapter];
let genericDoesNotSupportDatastoreMethodsError = adapterSupportVerify(adapter, datastore.name);

// Verifiy driver query capability
var driverMethodNames = _.keys(datastore.driver);

var isConnectable = _.difference([
'createManager',
'destroyManager',
'getConnection',
'releaseConnection'
], driverMethodNames)
.length === 0;

var isQueryable = isConnectable && _.difference([
'sendNativeQuery',
'compileStatement',
'parseNativeQueryResult',
'parseNativeQueryError'
], driverMethodNames)
.length === 0;

if (genericDoesNotSupportDatastoreMethodsError) {
return done(genericDoesNotSupportDatastoreMethodsError);
}

var options = {
manager: datastore.manager,
driver: datastore.driver,
connection: undefined,

nativeQuery: _nativeQuery,
valuesToEscape: _valuesToEscape,
meta: _meta,
};

if (more) {
_.extend(options, more);
}

if (!isQueryable) {
return done(flaverr('E_NOT_SUPPORTED', new Error(
'Cannot use `.sendNativeQuery()` with this datastore because the underlying adapter '+
'does not implement the "queryable" interface layer. This may be because of a '+
'natural limitation of the technology, or it could just be that the adapter\'s '+
'developer(s) have not finished implementing one or more driver methods.'
)));
}

if (!options.nativeQuery) {
return done(flaverr({ name: 'UsageError' }, new Error(
'Invalid native query passed in to `.sendNativeQuery()`. (Must be truthy-- e.g. "SELECT * FROM foo")'
)));
}

helpSendNativeQuery(options, done);
});
}, explicitCb, {

meta: function(_meta){
options.meta = _meta;
return this;
},

usingConnection: function(_usingConnection){
options.connection = _usingConnection;
return this;
},

});//</parley()>

};
11 changes: 9 additions & 2 deletions lib/internal/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var removeFromCollection = require('./querys/remove-from-collection');
var replaceCollection = require('./querys/replace-collection');
var addToCollection = require('./querys/add-to-collection');
var validate = require('./querys/validate');
const sendNativeQuery = require('../internal/helpers/send-native-query');

module.exports = {
create: create,
Expand All @@ -35,10 +36,16 @@ module.exports = {
getDatastore: function(req) {
const _self = this;
_self.datastore = _self.s_datastore;
if(!req) return _self.s_getDatastore();
if (!req) {
let datastore = _self.s_getDatastore();
datastore.sendNativeQuery = sendNativeQuery.bind(datastore);
return datastore;
}
return new Promise(async (resolve,reject) => {
_self.tenancy(req).then((model_in) => {
resolve(model_in.s_getDatastore());
let datastore = model_in.s_getDatastore();
datastore.sendNativeQuery = sendNativeQuery.bind(datastore);
resolve(datastore);
});
});
}
Expand Down
5 changes: 2 additions & 3 deletions lib/internal/querys/archive.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,6 @@ module.exports = function archive(/* criteria, explicitCbMaybe, metaContainer */
query.meta = args[1];
}



// ██████╗ ███████╗███████╗███████╗██████╗
// ██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗
// ██║ ██║█████╗ █████╗ █████╗ ██████╔╝
Expand Down Expand Up @@ -192,6 +190,7 @@ module.exports = function archive(/* criteria, explicitCbMaybe, metaContainer */
// This ensures a normalized format.
// Make the tenancy decition
_self.tenancy(req, ignore_tenancy).then(model => {

try {
forgeStageTwoQuery(query, orm);
} catch (err) {
Expand Down Expand Up @@ -248,7 +247,7 @@ module.exports = function archive(/* criteria, explicitCbMaybe, metaContainer */
// Then just leverage those methods here in `.archive()`.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


// console.log(WLModel);
// ╔═╗═╗ ╦╔═╗╔═╗╦ ╦╔╦╗╔═╗ ┌─┐┬┌┐┌┌┬┐ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
// ║╣ ╔╩╦╝║╣ ║ ║ ║ ║ ║╣ ├┤ ││││ ││ │─┼┐│ │├┤ ├┬┘└┬┘
// ╚═╝╩ ╚═╚═╝╚═╝╚═╝ ╩ ╚═╝ └ ┴┘└┘─┴┘ └─┘└└─┘└─┘┴└─ ┴
Expand Down
1 change: 1 addition & 0 deletions lib/internal/querys/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ module.exports = function create(newRecord, explicitCbMaybe, metaContainer) {
// Forge a stage 2 query (aka logical protostatement)
// This ensures a normalized format.
_self.tenancy(req, ignore_tenancy).then(model => {

try {
forgeStageTwoQuery(query, orm);
} catch (e) {
Expand Down
1 change: 0 additions & 1 deletion lib/internal/querys/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ var _ = require('@sailshq/lodash');
var flaverr = require('flaverr');
var normalizeValueToSet = require('waterline/lib/waterline/utils/query/private/normalize-value-to-set');
var verifyModelMethodContext = require('waterline/lib/waterline/utils/query/verify-model-method-context');
var _datasources = require('../../methods/getdatasources');


/**
Expand Down
Loading

0 comments on commit d479e19

Please sign in to comment.