How to call remote method of one persisted model inside another remote method of another persisted model - loopbackjs

here's what I tried in model1.js:
model1.remotemethod1 = function(id, data, cb) {
var model2 = app.models.model2;
model2.remotemethod2(id, data).then(response => {
cb(null, true);
});
};
this is my model2.js :
it has the definition of remotemethod2 .
'use strict';
module.exports = function(model2) {
model2.remotemethod2 = function(id, data, cb) {
var promise;
let tags = data.tags ? data.tags.slice() : [];
delete data.categories;
delete data.tags;
promise = model2.upsertWithWhere({
or: [
{barcode: data.barcode},
{id: data.id},
],
}, data);
promise.then(function(model2) {
model2.tags.destroyAll().then(function() {
for (let i = 0; i < tags.length; i++) {
model2.tags.add(tags[i]);
}
cb(null, model2);
});
});
};
};
But it dos not work !
I think that app.models.model2 does not give me the model with its remote methods ! maybe I should get an instance of the model2 !

var (VAR-NAME)= (CURRENT-MODEL).app.models.(ANOTHER_MODEL);
you now can use the other model by calling one of its methods for example
EX:
VAR-NAME.create();

Declare remotemethod1 in server.js app.start and you'll have access to the correct app.models.model2 and you will be able to use its remote method.
app.start = function() {
model1.remotemethod1 = (id, data, cb) => {
var model2 = app.models.model2;
model2.remotemethod2(id, data).then(response => {
cb(null, true);
});
};
model1.remoteMethod(
'remotemethod1', {
http: { path: '/remotemethod1', verb: 'post', status: 200, errorStatus: 400 },
accepts: [{arg: 'id', type: 'number'}, {arg: 'id', type: 'object'}],
returns: {arg: 'status', type : 'string' }
}) ;
}
// The rest of app.start...
EDIT you can also create the remote method will the correct app context with a file located in myprojectname/server/boot
`module.exports(app) {
/* Create remote methods here */
}`

Related

How to apply filters to Embedded Power BI Visual?

I am working on a project where we need to embed a Power BI Report but not with the EmbedType of a Report - it should be visual. What I did is to embed each visual of the report into a separate DIV and what I have to do now is to apply the filters on each.
Sample Code (cshtml):
var embedContainer = '';
var pageName = '';
var visualName = '';
var type = '';
var DisplayObject = [
{
embedContainer: $('#report-container_hSlicer')[0],
pageName: 'ReportSection',
visualName: '0e2d3a2ad25c8c8c224b',
type: 'visual',
},
{
embedContainer: $('#report-container_lSlicer')[0],
pageName: 'ReportSection',
visualName: "1f1841a2b8b3414a4318",
type: 'visual',
},
{
embedContainer: $('#report-container_fSlicer')[0],
pageName: 'ReportSection',
visualName: "3acac86be0dd995b34b1",
type: 'visual',
},
{
embedContainer: $('#report-container_index')[0],
pageName: 'ReportSection',
visualName: '802df8d5bc156f326b5a',
type: 'visual',
},
{
embedContainer: $('#report-container_cbCity')[0],
pageName: 'ReportSection',
visualName: "3cd8ddf8eb40dcc35d4d",
type: 'visual',
},
{
embedContainer: $('#report-container_All')[0],
type: 'report',
},
]
On the JS for embedding:
var resultModel = {};
var report = null;
var pages = [];
var config = {};
var reports = [];
function getParameters() {
var model = {
"workspace": "8b4f5f85-02a4-4afe-9104-3d4929d025c4",
"report": "038b5e5b-4b51-40ae-91f6-580c745b32c3"
}
$.ajax({
type: "POST",
url: "/embedinfo/getembedinfo",
contentType: 'application/json',
data: JSON.stringify(model),
success: function (data) {
resultModel = {
token: data.embedToken.token,
embedUrl: data.embedReport[0].embedUrl,
reportID: data.embedReport[0].reportId,
type: data.type
}
//Display each visual
DisplayObject.forEach(e => {
embedContainer = e.embedContainer;
pageName = e.pageName;
visualName = e.visualName;
type = e.type;
embedPowerBIReport(resultModel.token, resultModel.embedUrl, resultModel.reportID, resultModel.type);
});
console.log(resultModel);
console.log('Got tokens');
},
error: function (err) {
alert(err);
}
});
};
let loadedResolve, reportLoaded = new Promise((res, rej) => { loadedResolve = res; });
let renderedResolve, reportRendered = new Promise((res, rej) => { renderedResolve = res; });
models = window["powerbi-client"].models;
function embedPowerBIReport(accessToken_, embedURL, embedReportID, TokenType) {
// Read embed application token
let accessToken = accessToken_;
// Read embed URL
let embedUrl = embedURL;
// Read report Id
let embedReportId = embedReportID;
// Read embed type from radio
let tokenType = TokenType;
// We give All permissions to demonstrate switching between View and Edit mode and saving report.
let permissions = models.Permissions.All;
// Create the embed configuration object for the report
// For more information see https://go.microsoft.com/fwlink/?linkid=2153590
if (type == 'report') {
config = {
type: type,
tokenType: tokenType == '0' ? models.TokenType.Aad : models.TokenType.Embed,
accessToken: accessToken,
embedUrl: embedUrl,
id: embedReportId,
settings: {
panes: {
filters: {
visible: false
},
}
}
};
}
else {
config = {
type: 'visual',
tokenType: tokenType == '0' ? models.TokenType.Aad : models.TokenType.Embed,
accessToken: accessToken,
embedUrl: embedUrl,
id: embedReportId,
permissions: permissions,
pageName: pageName,
visualName: visualName,
navContentPaneEnabled: false,
settings: {
panes: {
filters: {
visible: false
},
}
}
};
}
// Get a reference to the embedded report HTML element
//let embedContainer = $('#report-container')[0];
// Embed the report and display it within the div container.
report = powerbi.embed(embedContainer, config);
// report.off removes all event handlers for a specific event
report.off("loaded");
// report.on will add an event handler
report.on("loaded", function () {
loadedResolve();
report.off("loaded");
});
// report.off removes all event handlers for a specific event
report.off("error");
report.on("error", function (event) {
console.log(event.detail);
});
// report.off removes all event handlers for a specific event
report.off("rendered");
// report.on will add an event handler
report.on("rendered", function () {
renderedResolve();
report.off("rendered");
});
reports.push(report);
}
async function main() {
await getParameters();
await reportLoaded;
console.log('Report Loaded');
// Insert here the code you want to run after the report is loaded
await reportRendered;
console.log('Report Rendered');
//console.log('got all page');
//var reportAll = reports.filter(function (report) {
// return report.embedtype == 'report';
//})[0];
console.log('got all page');
// Insert here the code you want to run after the report is rendered
const filter = {
$schema: "http://powerbi.com/product/schema#basic",
target: {
table: "tblLifeStage",
column: "Life Stage"
},
operator: "In",
values: ["F2 - Upscale Earners"],
filterType: models.FilterType.BasicFilter,
requireSingleSelection: true
};
//// Retrieve the page collection and get the visuals for the active page.
//pages = await reportAll.getPages();
//// Retrieve the active page.
//let page = pages.filter(function (page) {
// return page.isActive;
//})[0];
//const visuals = await page.getVisuals();
//console.log(visuals);
//// Retrieve the target visual.
//let slicer = visuals.filter(function (visual) {
// return visual.type === "slicer" && visual.name === "1f1841a2b8b3414a4318";
//})[0];
//console.log(slicer);
// Set the slicer state which contains the slicer filters.
for (const report of reports) {
console.log(report);
await report.setSlicerState({ filters: [filter] });
console.log("slicer was set.");
};
};
//Calling Async function
main();
We can Apply Visual level filters to every Visual. You can use updateFilters to set new filter to the Visual our Update the filters.
Simply add this piece of code On the js for embedding in the display each Visual section or you use the variable visualName and apply filters later to the visual.
await visualName.updateFilters(models.FiltersOperations.Replace, filtersArray);
Reference:
https://learn.microsoft.com/javascript/api/overview/powerbi/control-report-filters#visual-level-filters

API request getting stuck in POSTMAN?

So, I am making an e-shop app which uses Mongo DB and Express JS as the backend. I have already created the productSchema, userSchema and the categorySchema and have coded for the appropriate GET requests.
I have made a jwt.js file which handles whether the the GET request should be allowed or not based on the token.
The code for jwt.js is given below
const { expressjwt } = require("express-jwt");
function authJwt() {
const secret = process.env.secret;
const api = process.env.API_URL;
return expressjwt({
secret,
algorithms: ["HS256"],
isRevoked: isRevoked,
}).unless({
path: [
{ url: /\/api\/v1\/products(.*)/, methods: ["GET", "OPTIONS"] },
{ url: /\/api\/v1\/categories(.*)/, methods: ["GET", "OPTIONS"] },
`${api}/users/login`,
`${api}/users/register`,
],
});
}
async function isRevoked(req, payload, done) {
if (!payload.isAdmin) {
done(null, true);
}
done();
}
module.exports = authJwt;
The code for products.js which handles the GET, POST, PUT and DELETE requests for the products database is given below.
const { Product } = require("../models/product");
const express = require("express");
const { Category } = require("../models/category");
const router = express.Router();
const mongoose = require("mongoose");
router.get(`/`, async (req, res) => {
// localhost:3000/api/v1/products?categories=2342342,234234
let filter = {};
if (req.query.categories) {
filter = { category: req.query.categories.split(",") };
}
const productList = await Product.find(filter).populate("category");
if (!productList) {
res.status(500).json({ success: false });
}
res.send(productList);
});
router.get(`/:id`, async (req, res) => {
const product = await Product.findById(req.params.id).populate("category");
if (!product) {
res.status(500).json({ success: false });
}
res.send(product);
});
router.post(`/`, async (req, res) => {
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
let product = new Product({
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
});
product = await product.save();
if (!product) return res.status(500).send("The product cannot be created");
res.send(product);
});
router.put("/:id", async (req, res) => {
if (!mongoose.isValidObjectId(req.params.id)) {
return res.status(400).send("Invalid Product Id");
}
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
const product = await Product.findByIdAndUpdate(
req.params.id,
{
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
},
{ new: true }
);
if (!product) return res.status(500).send("the product cannot be updated!");
res.send(product);
});
router.delete("/:id", (req, res) => {
Product.findByIdAndRemove(req.params.id)
.then((product) => {
if (product) {
return res
.status(200)
.json({ success: true, message: "the product is deleted!" });
} else {
return res
.status(404)
.json({ success: false, message: "product not found!" });
}
})
.catch((err) => {
return res.status(500).json({ success: false, error: err });
});
});
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count) => count);
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount,
});
});
router.get(`/get/featured/:count`, async (req, res) => {
const count = req.params.count ? req.params.count : 0;
const products = await Product.find({ isFeatured: true }).limit(+count);
if (!products) {
res.status(500).json({ success: false });
}
res.send(products);
});
module.exports = router;
Now, the codes for the users.js and categories.js are similar and thus I am not sharing it.
I am getting the problem when doing GET request for products using POSTMAN API. Even though I am passing the correct token using BEARER TOKEN field in the POSTMAN API, it is getting stuck at sending request. When I delete the isRevoked part, everything works fine, but then again I can't control the get request based on the isAdmin part. So, the problem is in the isRevoked part. But, what exactly is the issue. It seems fine to me logically.
the problem could arise from so many things, could not say without a deeper look at your code but, here are some suggestions:
should isRevoked be async?
does your payload contains isAdmin?
and if so, inside the if statement should be done(null, false) after the if statement you should get a userid or any sort of unique fields such as userEmail, ..., then use your userModel to query the user document so that your last done() be done(null, user)

How to receive Post request body and pass that body to my function in loopback

i want to create dynamic model, repository and controller
export async function dynamicModelsDemo(app: any, modelData: any): Promise<boolean> {
console.log("ModelData",modelData);
// assume that this def can be created dynamically (at runtime), e.g. from database info
const modelDef = new ModelDefinition({
name: 'contact',
properties: {
id: {
type: 'Number',
required: true,
length: null,
precision: 10,
scale: 0,
id: 1,
},
name: {
type: 'String',
required: false,
length: 512,
precision: null,
scale: null,
},
},
});
// tryin' to extend Entity with new fields
const DynamicModel = defineModelClass<typeof Entity, {id: number; title?: string}>(
Entity,
modelDef,
);
const BookRepository = defineCrudRepositoryClass(DynamicModel);
inject(`datasources.memory`)(BookRepository, undefined, 0);
const repoBinding = app.repository(BookRepository);
const basePath = '/contact';
const DynamicController0 = defineCrudRestController(DynamicModel, {basePath});
inject(repoBinding.key)(DynamicController0, undefined, 0);
app.controller(DynamicController0);
console.log(basePath);
return new Promise(function (resolve, reject) {
resolve(true);
});
}
i need help that how should i create Post method which would receive request body and that body would pass to my function above i mentioned,
Currently i'm calling dynamicModelsDemo function by this endpoint,
#get('/ping/build', {
modelData : {},
responses: {
'200': {
description: 'Test models assemble',
},
},
})
async build(): Promise<boolean> {
return dynamicModelsDemo(this.localApp,this.modelData);
}
i want to convert this #get to #post so i can pass my requested body to this function..
This is working so fine, I thing this is what I was looking for:
#post('ping/createobject')
async createObject(
#requestBody() model: any
):Promise<boolean> {
return dynamicModelsDemo(this.localApp,model);
}

Foreign Key with Sequelize not working as expected

I was trying to create an association between two tables and I wanted to add a foreign key.
The two models are User and Companies
User.associate = (models) => {
User.belongsTo(models.Companies, { foreignKey: 'Company' });
};
My expectation of the code above was that a Company ID field gets added in the user table which references the Company ID of the Companies table.
On running the code above, I don't see any additional columns getting created. I tried checking if a foreign key association is created in the DB and that also is missing.
However, if I try to add a column with the same name while keeping the association code, I get a name conflict. This seems to suggest that the association is getting created but I am unable to see it.
Could someone help me understand what I am doing wrong? Thanks for the help!
models/company.js
module.exports = (sequelize, DataTypes) => {
var Company = sequelize.define('company', {
company: { type: DataTypes.STRING, primaryKey: true },
});
Company.associate = (models) => {
Company.hasMany(models.user, { as: 'users' });
};
Company.sync();
return Company;
};
models/user.js
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {
id: { type: DataTypes.UUID, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false }
});
User.associate = (models) => {
User.belongsTo(models.company);
};
User.beforeCreate((user, _ ) => {
user.id = uuid();
return user;
});
User.sync();
return User;
};
models/index.js
'use strict';
var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var basename = path.basename(__filename);
var env = process.env.NODE_ENV || 'development';
// var config = require(__dirname + '/../config/config.js')[env];
var db = {};
// if (config.use_env_variable) {
// var sequelize = new Sequelize(process.env[config.use_env_variable], config);
// } else {
// var sequelize = new Sequelize(config.database, config.username, config.password, config);
// }
const sequelize = new Sequelize('postgres://postgres:user#localhost:5432/mydb');
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
var model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
I was able to get this resolved.
The issue was with regard to the sequence in which the sync was called. In my original code, I was calling sync inside each model. Even though I added the options force and alter, I think the foreign keys were not getting added. So, I removed the sync code from inside the models, and added it in a separate loop inside index.js.
This gave me a new issue. Tables were getting created in an order that is not consistent with the order in which tables should be created for foreign keys to work since tables should pre-exist. I resolved it by manually providing the sequence of sync and now I see the columns getting created.
To summarise: model defn -> model association -> model sync in sequence
Thank you for your suggestions, members of SO.
Your model is fine! you must remove sync from models file , then check migration file for models with foreign key that foregin key is there,
for Migration User :
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.UUID
},
name: {
type: Sequelize.STRING
},
companyId: {
type: Sequelize.UUID,
references: {
model: 'Company',// company migration define
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
for create automate table from index.js and models you must install sequelize-cli
by type npm install --save sequelize-cli
then you must run this command for create models table in db
sequelize db:migrate
By using foreignKey: 'Company' you are telling it to associate with a column named Company. You typically also want to use singular table names, so company with an association of companies. By default Sequelize will use the primary key for the association, so you only need to specify foreignKey if you want to change it or set other parameters.
const User = sequelize.define(
'user',
{ /* columns */ },
{ /* options */ }
);
User.associate = (models) => {
User.belongsTo(models.Company);
};
const Company = sequelize.define(
'company',
{ /* columns */ },
{ /* options */ }
);
Company.associate = (models) => {
Company.hasMany(models.User, { as: 'users' });
};
This will create the following tables Company (id) and User (id, company_id).
Query all User records associated to a single Company:
const user = await User.findAll({ include: { model: Company } });
/*
user = {
id: 1,
company_id: 1,
company: {
id: 1,
},
};
*/
Query all Company records with multiple associated User records via users:
const company = await User.findAll({ include: { model: User, as: 'users' } });
/*
company = {
id: 1,
users: [{
id: 1
company_id: 1,
}],
};
*/
My guess is that the associate method is not getting called, and therefore, your association does not get created. Keep in mind that associate is not a built-in Sequelize method, but it is just a pattern used by the community. (More info on this thread)
There are various approaches to handle calling associate, here is one example. You have a models.js file that handles your association and you initialize that inside your main app.js file.
// app.js (aka your main application)
const models = require('./models')(sequelize, DataTypes);
// models.js
module.exports = (sequelize, DataTypes) => {
const models = {
user: require('./userModel')(sequelize, DataTypes),
company: require('./companyModel')(sequelize, DataTypes)
};
Object.keys(models).forEach(key => {
if (models[key] && models[key].associate) {
models[key].associate(models);
}
});
};
// companyModel.js
module.exports = (sequelize, DataTypes) => {
var Company = sequelize.define('company', {...});
Company.associate = (models) => {
Company.hasMany(models.user, { as: 'users' });
};
Company.sync();
return Company;
};
// userModel.js
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {...});
User.sync();
return User;
};
Also, FYI, You probably know this but sync should only be used for experimenting or testing, not for a production app.

Apollo: Update React Props on Subscription Update?

Looking at the Apollo docs example code for subscriptions, I am not yet seeing how to update the React props with the subscription results.
From http://dev.apollodata.com/react/subscriptions.html:
Here is a regular query:
import { CommentsPage } from './comments-page.js';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
const COMMENT_QUERY = gql`
query Comment($repoName: String!) {
entry(repoFullName: $repoName) {
comments {
id
content
}
}
}
`;
const withData = graphql(COMMENT_QUERY, {
name: 'comments',
options: ({ params }) => ({
variables: {
repoName: `${params.org}/${params.repoName}`
},
})
});
export const CommentsPageWithData = withData(CommentsPage);
Now, let’s add the subscription.
Note that this sample code appears to leave out this part of the props code for usual queries - from http://dev.apollodata.com/react/queries.html:
props: ({ ownProps, data: { loading, currentUser, refetch } }) => ({
userLoading: loading,
user: currentUser,
refetchUser: refetch,
}),
...which AFAIK is the correct way to update the data props on my React component and trigger a page refresh.
Here is the complete subscription code sample from http://dev.apollodata.com/react/subscriptions.html:
const withData = graphql(COMMENT_QUERY, {
name: 'comments',
options: ({ params }) => ({
variables: {
repoName: `${params.org}/${params.repoName}`
},
}),
props: props => {
return {
subscribeToNewComments: params => {
return props.comments.subscribeToMore({
document: COMMENTS_SUBSCRIPTION,
variables: {
repoName: params.repoFullName,
},
updateQuery: (prev, {subscriptionData}) => {
if (!subscriptionData.data) {
return prev;
}
const newFeedItem = subscriptionData.data.commentAdded;
return Object.assign({}, prev, {
entry: {
comments: [newFeedItem, ...prev.entry.comments]
}
});
}
});
}
};
},
});
How do I get the code shown here, to update the data props on my React component and trigger a page refresh, when the results come in from the non-subscription query COMMENT_QUERY?
Thanks to #neophi on the Apollo Slack for this answer!
const withDataAndSubscription = graphql(GETIMS_QUERY, {
options({toID}) {
console.log(GETIMS_QUERY);
const fromID = Meteor.userId();
return {
fetchPolicy: 'cache-and-network',
variables: {fromID: `${fromID}`, toID: `${toID}`}
};
}
,
props: props => {
return {
loading: props.data.loading,
instant_message: props.data.instant_message,
subscribeToMore: props.data.subscribeToMore,
subscribeToNewIMs: params => {
const fromID = Meteor.userId();
const toID = params.toID;
return props.data.subscribeToMore({
document: IM_SUBSCRIPTION_QUERY,
variables: {fromID: `${fromID}`, toID: `${toID}`},
updateQuery: (previousResult, {subscriptionData}) => {
if (!subscriptionData.data) {
return previousResult;
}
const newMsg = subscriptionData.data.createIM;
return update(previousResult, {
instant_message: {
$push: [newMsg],
},
});
}
});
}
};
},
})
;