2018-01-03 04:05:53 +00:00
|
|
|
// Copyright IBM Corp. 2016,2018. All Rights Reserved.
|
2016-09-20 08:54:41 +00:00
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var expect = require('chai').expect;
|
|
|
|
var loopback = require('..');
|
2017-02-09 15:17:10 +00:00
|
|
|
var supertest = require('supertest');
|
2016-09-20 08:54:41 +00:00
|
|
|
|
|
|
|
describe('OptionsFromRemotingContext', function() {
|
|
|
|
var app, request, accessToken, userId, Product, actualOptions;
|
|
|
|
|
|
|
|
beforeEach(setupAppAndRequest);
|
|
|
|
beforeEach(resetActualOptions);
|
|
|
|
|
|
|
|
context('when making updates via REST', function() {
|
|
|
|
beforeEach(observeOptionsBeforeSave);
|
|
|
|
|
|
|
|
it('injects options to create()', function() {
|
|
|
|
return request.post('/products')
|
|
|
|
.send({name: 'Pen'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to patchOrCreate()', function() {
|
|
|
|
return request.patch('/products')
|
|
|
|
.send({id: 1, name: 'Pen'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to replaceOrCreate()', function() {
|
|
|
|
return request.put('/products')
|
|
|
|
.send({id: 1, name: 'Pen'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to patchOrCreateWithWhere()', function() {
|
|
|
|
return request.post('/products/upsertWithWhere?where[name]=Pen')
|
|
|
|
.send({name: 'Pencil'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to replaceById()', function() {
|
|
|
|
return Product.create({id: 1, name: 'Pen'})
|
|
|
|
.then(function(p) {
|
|
|
|
return request.put('/products/1')
|
|
|
|
.send({name: 'Pencil'})
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to prototype.patchAttributes()', function() {
|
|
|
|
return Product.create({id: 1, name: 'Pen'})
|
|
|
|
.then(function(p) {
|
|
|
|
return request.patch('/products/1')
|
|
|
|
.send({name: 'Pencil'})
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to updateAll()', function() {
|
|
|
|
return request.post('/products/update?where[name]=Pen')
|
|
|
|
.send({name: 'Pencil'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context('when deleting via REST', function() {
|
|
|
|
beforeEach(observeOptionsBeforeDelete);
|
|
|
|
|
|
|
|
it('injects options to deleteById()', function() {
|
|
|
|
return Product.create({id: 1, name: 'Pen'})
|
|
|
|
.then(function(p) {
|
|
|
|
return request.delete('/products/1').expect(200);
|
|
|
|
})
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context('when querying via REST', function() {
|
|
|
|
beforeEach(observeOptionsOnAccess);
|
|
|
|
beforeEach(givenProductId1);
|
|
|
|
|
|
|
|
it('injects options to find()', function() {
|
|
|
|
return request.get('/products').expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to findById()', function() {
|
|
|
|
return request.get('/products/1').expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to findOne()', function() {
|
|
|
|
return request.get('/products/findOne?where[id]=1').expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to exists()', function() {
|
|
|
|
return request.head('/products/1').expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to count()', function() {
|
|
|
|
return request.get('/products/count').expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context('when invoking prototype methods', function() {
|
|
|
|
beforeEach(observeOptionsOnAccess);
|
|
|
|
beforeEach(givenProductId1);
|
|
|
|
|
|
|
|
it('injects options to sharedCtor', function() {
|
|
|
|
Product.prototype.dummy = function(cb) { cb(); };
|
|
|
|
Product.remoteMethod('prototype.dummy', {});
|
|
|
|
return request.post('/products/1/dummy').expect(204)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Catch: because relations methods are defined on "modelFrom",
|
|
|
|
// they will invoke createOptionsFromRemotingContext on "modelFrom" too,
|
|
|
|
// despite the fact that under the hood a method on "modelTo" is called.
|
|
|
|
|
|
|
|
context('hasManyThrough', function() {
|
|
|
|
var Category, ThroughModel;
|
|
|
|
|
|
|
|
beforeEach(givenCategoryHasManyProductsThroughAnotherModel);
|
|
|
|
beforeEach(givenCategoryAndProduct);
|
|
|
|
|
|
|
|
it('injects options to findById', function() {
|
|
|
|
observeOptionsOnAccess(Product);
|
|
|
|
return request.get('/categories/1/products/1').expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to destroyById', function() {
|
|
|
|
observeOptionsBeforeDelete(Product);
|
|
|
|
return request.del('/categories/1/products/1').expect(204)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to updateById', function() {
|
|
|
|
observeOptionsBeforeSave(Product);
|
|
|
|
return request.put('/categories/1/products/1')
|
|
|
|
.send({description: 'a description'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
context('through-model operations', function() {
|
|
|
|
it('injects options to link', function() {
|
|
|
|
observeOptionsBeforeSave(ThroughModel);
|
|
|
|
return Product.create({id: 2, name: 'Car2'})
|
|
|
|
.then(function() {
|
|
|
|
return request.put('/categories/1/products/rel/2')
|
|
|
|
.send({description: 'a description'})
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to unlink', function() {
|
|
|
|
observeOptionsBeforeDelete(ThroughModel);
|
|
|
|
return request.del('/categories/1/products/rel/1').expect(204)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to exists', function() {
|
|
|
|
observeOptionsOnAccess(ThroughModel);
|
|
|
|
return request.head('/categories/1/products/rel/1').expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context('scope operations', function() {
|
|
|
|
it('injects options to get', function() {
|
|
|
|
observeOptionsOnAccess(Product);
|
|
|
|
return request.get('/categories/1/products').expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to create', function() {
|
|
|
|
observeOptionsBeforeSave(Product);
|
|
|
|
return request.post('/categories/1/products')
|
|
|
|
.send({name: 'Pen'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to delete', function() {
|
|
|
|
observeOptionsBeforeDelete(ThroughModel);
|
|
|
|
return request.del('/categories/1/products').expect(204)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to count', function() {
|
|
|
|
observeOptionsOnAccess(ThroughModel);
|
|
|
|
return request.get('/categories/1/products/count').expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function givenCategoryHasManyProductsThroughAnotherModel() {
|
|
|
|
Category = app.registry.createModel(
|
|
|
|
'Category',
|
|
|
|
{name: String},
|
2018-08-08 15:22:20 +00:00
|
|
|
{forceId: false, replaceOnPUT: true}
|
|
|
|
);
|
2016-09-20 08:54:41 +00:00
|
|
|
|
|
|
|
app.model(Category, {dataSource: 'db'});
|
|
|
|
// This is a shortcut for creating CategoryProduct "through" model
|
|
|
|
Category.hasAndBelongsToMany(Product);
|
|
|
|
|
|
|
|
Category.createOptionsFromRemotingContext = function(ctx) {
|
|
|
|
return {injectedFrom: 'Category'};
|
|
|
|
};
|
|
|
|
|
|
|
|
ThroughModel = app.registry.getModel('CategoryProduct');
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenCategoryAndProduct() {
|
|
|
|
return Category.create({id: 1, name: 'First Category'})
|
|
|
|
.then(function(cat) {
|
|
|
|
return cat.products.create({id: 1, name: 'Pen'});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function expectOptionsInjectedFromCategory() {
|
|
|
|
expect(actualOptions).to.have.property('injectedFrom', 'Category');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
context('hasOne', function() {
|
|
|
|
var Category;
|
|
|
|
|
|
|
|
beforeEach(givenCategoryHasOneProduct);
|
|
|
|
beforeEach(givenCategoryId1);
|
|
|
|
|
|
|
|
it('injects options to get', function() {
|
|
|
|
observeOptionsOnAccess(Product);
|
|
|
|
return givenProductInCategory1()
|
|
|
|
.then(function() {
|
|
|
|
return request.get('/categories/1/product').expect(200);
|
|
|
|
})
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to create', function() {
|
|
|
|
observeOptionsBeforeSave(Product);
|
|
|
|
return request.post('/categories/1/product')
|
|
|
|
.send({name: 'Pen'})
|
|
|
|
.expect(200)
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to update', function() {
|
|
|
|
return givenProductInCategory1()
|
|
|
|
.then(function() {
|
|
|
|
observeOptionsBeforeSave(Product);
|
|
|
|
return request.put('/categories/1/product')
|
|
|
|
.send({description: 'a description'})
|
|
|
|
.expect(200);
|
|
|
|
})
|
|
|
|
.then(expectInjectedOptions);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('injects options to destroy', function() {
|
|
|
|
observeOptionsBeforeDelete(Product);
|
|
|
|
return givenProductInCategory1()
|
|
|
|
.then(function() {
|
|
|
|
return request.del('/categories/1/product').expect(204);
|
|
|
|
})
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
function givenCategoryHasOneProduct() {
|
|
|
|
Category = app.registry.createModel(
|
|
|
|
'Category',
|
|
|
|
{name: String},
|
2018-08-08 15:22:20 +00:00
|
|
|
{forceId: false, replaceOnPUT: true}
|
|
|
|
);
|
2016-09-20 08:54:41 +00:00
|
|
|
|
|
|
|
app.model(Category, {dataSource: 'db'});
|
|
|
|
Category.hasOne(Product);
|
|
|
|
|
|
|
|
Category.createOptionsFromRemotingContext = function(ctx) {
|
|
|
|
return {injectedFrom: 'Category'};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenCategoryId1() {
|
|
|
|
return Category.create({id: 1, name: 'First Category'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenProductInCategory1() {
|
|
|
|
return Product.create({id: 1, name: 'Pen', categoryId: 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
function expectOptionsInjectedFromCategory() {
|
|
|
|
expect(actualOptions).to.have.property('injectedFrom', 'Category');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
context('belongsTo', function() {
|
|
|
|
var Category;
|
|
|
|
|
|
|
|
beforeEach(givenCategoryBelongsToProduct);
|
|
|
|
|
|
|
|
it('injects options to get', function() {
|
|
|
|
observeOptionsOnAccess(Product);
|
|
|
|
return Product.create({id: 1, name: 'Pen'})
|
|
|
|
.then(function() {
|
|
|
|
return Category.create({id: 1, name: 'a name', productId: 1});
|
|
|
|
})
|
|
|
|
.then(function() {
|
|
|
|
return request.get('/categories/1/product').expect(200);
|
|
|
|
})
|
|
|
|
.then(expectOptionsInjectedFromCategory);
|
|
|
|
});
|
|
|
|
|
|
|
|
function givenCategoryBelongsToProduct() {
|
|
|
|
Category = app.registry.createModel(
|
|
|
|
'Category',
|
|
|
|
{name: String},
|
2018-08-08 15:22:20 +00:00
|
|
|
{forceId: false, replaceOnPUT: true}
|
|
|
|
);
|
2016-09-20 08:54:41 +00:00
|
|
|
|
|
|
|
app.model(Category, {dataSource: 'db'});
|
|
|
|
Category.belongsTo(Product);
|
|
|
|
|
|
|
|
Category.createOptionsFromRemotingContext = function(ctx) {
|
|
|
|
return {injectedFrom: 'Category'};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenCategoryId1() {
|
|
|
|
return Category.create({id: 1, name: 'First Category'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenProductInCategory1() {
|
|
|
|
return Product.create({id: 1, name: 'Pen', categoryId: 1});
|
|
|
|
}
|
|
|
|
|
|
|
|
function expectOptionsInjectedFromCategory() {
|
|
|
|
expect(actualOptions).to.have.property('injectedFrom', 'Category');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function setupAppAndRequest() {
|
|
|
|
app = loopback({localRegistry: true});
|
|
|
|
app.dataSource('db', {connector: 'memory'});
|
|
|
|
|
|
|
|
Product = app.registry.createModel(
|
|
|
|
'Product',
|
|
|
|
{name: String},
|
2018-08-08 15:22:20 +00:00
|
|
|
{forceId: false, replaceOnPUT: true}
|
|
|
|
);
|
2016-09-20 08:54:41 +00:00
|
|
|
|
|
|
|
Product.createOptionsFromRemotingContext = function(ctx) {
|
|
|
|
return {injectedFrom: 'Product'};
|
|
|
|
};
|
|
|
|
|
|
|
|
app.model(Product, {dataSource: 'db'});
|
|
|
|
|
|
|
|
app.use(loopback.rest());
|
|
|
|
request = supertest(app);
|
|
|
|
}
|
|
|
|
|
|
|
|
function resetActualOptions() {
|
|
|
|
actualOptions = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function observeOptionsBeforeSave() {
|
|
|
|
var Model = arguments[0] || Product;
|
|
|
|
Model.observe('before save', function(ctx, next) {
|
|
|
|
actualOptions = ctx.options;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function observeOptionsBeforeDelete() {
|
|
|
|
var Model = arguments[0] || Product;
|
|
|
|
Model.observe('before delete', function(ctx, next) {
|
|
|
|
actualOptions = ctx.options;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function observeOptionsOnAccess() {
|
|
|
|
var Model = arguments[0] || Product;
|
|
|
|
Model.observe('access', function(ctx, next) {
|
|
|
|
actualOptions = ctx.options;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function givenProductId1() {
|
|
|
|
return Product.create({id: 1, name: 'Pen'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function expectInjectedOptions(name) {
|
|
|
|
expect(actualOptions).to.have.property('injectedFrom');
|
|
|
|
}
|
|
|
|
});
|