I am embedding a power bi report using pupeteer/chromium quite happily and then save that as a screenshot/pdf. However, a late breaking requirement requires me to be able to hook the report's onloaded event.
I have the following code snippet which is the template I use to hook up the event; the report is embedding, but the 'report.on' event is not firing, (In reality I'm trying to set some visuals and other stuff, not just log text.)
await page.evaluate((configdata) => {
const models = window['powerbi-client'].models;
const config = {
...
};
const report = powerbi.embed(reportContainer, config)
report.on('loaded', function () {
console.log('loaded report')
});
},
configdata);
I've looked at "exposeFunction()" but couldn't get it hooked to this event (or others).
Would some please tell me what I'm missing; there must be way to do this, but I'm missing how to tie the report object (instantiated from within the IFrame, to it's event from withing the puppeteer function. However, JS/Node is not my primary discipline, hell it's not even my second!
PS: I know (and have got working) passing filters into to the configuration; but that is not quite good enough from the aethetics point of view (on screen visuals are not set!)
Any help/pointers - very greatly appreciated
We've kept with passing the filters into the configuration whne embedding the report.
short and simple.
To answer the question, you can use page.evaluate and create a Promise which will be resolved when the embed loaded event will be triggered. Then you can await for your loadEmbed function:
async function loadEmbed(page, config) {
return page.evaluate(async (config) => {
await new Promise((resolve, reject) => {
try {
var embedContainer = $('#embedContainer')[0];
var embed = powerbi.embed(embedContainer, config);
embed.off("loaded");
embed.on("loaded", function () {
resolve(true);
});
} catch (err) {
resolve(false);
}
});
}, config);
}
Related
Most of the information out there about Apollo Client and GraphQL queries is about fetching data and immediately rendering something.
What about the common use case where I want to fetch data to, let say, update the state in which I clearly don't need to render JSX, I just want to run Javascript code.
Use the following code snippet as an example
onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
console.info('variables ready', variables)
return (
<Query query={RESOLVE_REF} variables={variables}>
{ ({ data, error }) => {
console.info('data response', data)
console.info('error response', error)
return data
}}
</Query>
)
}
Apollo forces me to use the Query component just to perform a query, even when I don't want to render anything. Also those console.info never log anything, but the variables ready text does appear.
I have found that the documentation is pretty clear on using the Query component, but obscure on every option which is different. I feel I'm missing something.
I'm also concerned about how Apollo doesn't seems respect the separation of responsibilities, apparently merging both data and presentation into a single responsibility (as is clear with the Query component), which in my current understanding is quite silly, but most likely I'm fucking things up.
Any insight is appreciated.
As long as you've configured and included an ApolloProvider at the top of your component tree, you can get your query instance using either the withApollo HOC, or the ApolloConsumer:
const MyComponent = ({ client }) => {
// use it!
}
withApollo(MyComponent)
<ApolloConsumer>
{client => (
// use it!
)}
</ApolloConsumer>
You can then use any of the methods that are available to the client instance, including query and mutation, both of which return a Promise that resolves to an ApolloQueryResult object that includes data and errors. The full documentation for the client's API can be found here. Your code would then look something like:
async onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
try {
const { data } = await this.props.client(RESOLVE_REF, { variables })
} catch (e) {
// Handle errors
}
}
I am trying to use AWS X-Ray in my SailsJS application. I noticed missing subsegments - I added custom traces via AWSXRay.captureAsyncFunc but noticed they are missing. After some closer inspection, I think they actually ended up in a different trace. Lets say I call login API then another API later. I notice my login API trace is weird
Notice theres calls quite abit after the request should have ended.
Those requests should actually be in another segment:
I'd think they should appear after find device subsegment. Why are segments scrambled like that?
My setup: in http.js,
const AWSXRay = require('aws-xray-sdk');
const xrayEnabled = process.env.AWS_XRAY === 'yes'
module.exports.http = {
middleware: {
order: [
'startRequestTimer',
'cookieParser',
'session',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'awsXrayStart',
'router',
'awsXrayEnd',
'www',
'favicon',
'404',
'500',
],
awsXrayStart: xrayEnabled ? AWSXRay.express.openSegment(`cast-${process.env.NODE_ENV || 'noenv'}`) : (req, res, next) => next(),
awsXrayEnd: xrayEnabled ? AWSXRay.express.closeSegment() : (req, res, next) => next(),
Then I wrapped my promises like:
instrumentPromise(promise, name, metadata = {}) {
if (this.isXrayEnabled()) {
return new Promise((resolve, reject) => {
AWSXRay.captureAsyncFunc(name, (subsegment) => {
if (!subsegment) console.warn(`[XRAY] Failed to instrument ${name}`)
Object.keys(metadata).forEach(k => {
if (subsegment) subsegment.addMetadata(k, metadata[k])
})
console.time(`[XRAY TIME] ${name}`)
promise
.then((data) => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
resolve(data)
})
.catch(err => {
if (subsegment) subsegment.close()
console.timeEnd(`[XRAY TIME] ${name}`)
reject(err)
})
})
})
}
return promise
}
Is there any information I am missing here? What am I doing wrong?
I tried manual mode and its alot more reliable but I have to manually pass segment around. Whats wrong with automatic mode? I am kind of guessing it does not work well with async nature nodejs? Like the SDK is not able to differenciate between the various async requests? And may close or track segments in the wrong places? That said ... its supposed to work with express, why isit not working as expected ...
Another thing is how will a shared mysql connection pool be tracked correctly by X-Ray? Different segments will be using the same mysql pool. I assume this will not work work well at all?
The issue you encounter seems to be related to how CLS handle context binding with Promise. There is a opt-in promise patch introduced in this PR https://github.com/aws/aws-xray-sdk-node/pull/11. It has full discussion around the repros and fixes. That should resolve the issue with subsegments being attached to the wrong trace.
The SDK does support capturing pool.query. You can see examples here https://www.npmjs.com/package/aws-xray-sdk-mysql.
I'm trying to test my 'Container' component which handles a forms logic. It is using vue-router and the vuex store to dispatch actions to get a forms details.
I have the following unit code which isn't working as intended:
it('On route enter, it should dispatch an action to fetch form details', () => {
const getFormDetails = sinon.stub();
const store = new Vuex.Store({
actions: { getFormDetails }
});
const wrapper = shallowMount(MyComponent, { store });
wrapper.vm.$options.beforeRouteEnter[0]();
expect(getFormDetails.called).to.be.true;
});
With the following component (stripped of everything because I don't think its relevant (hopefully):
export default {
async beforeRouteEnter(to, from, next) {
await store.dispatch('getFormDetails');
next();
}
};
I get the following assertion error:
AssertionError: expected false to be true
I'm guessing it is because I am not mounting the router in my test along with a localVue. I tried following the steps but I couldn't seem to get it to invoke the beforeRouteEnter.
Ideally, I would love to inject the router with a starting path and have different tests on route changes. For my use case, I would like to inject different props/dispatch different actions based on the component based on the path of the router.
I'm very new to Vue, so apologies if I'm missing something super obvious and thank you in advance for any help! 🙇🏽
See this doc: https://lmiller1990.github.io/vue-testing-handbook/vue-router.html#component-guards
Based on the doc, your test should look like this:
it('On route enter, it should dispatch an action to fetch form details', async () => {
const getFormDetails = sinon.stub();
const store = new Vuex.Store({
actions: { getFormDetails }
});
const wrapper = shallowMount(MyComponent, { store });
const next = sinon.stub()
MyComponent.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next)
await wrapper.vm.$nextTick()
expect(getFormDetails.called).to.be.true;
expect(next.called).to.be.true
});
A common pattern with beforeRouteEnter is to call methods directly at the instantiated vm instance. The documentation states:
The beforeRouteEnter guard does NOT have access to this, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
However, you can access the instance by passing a callback to next. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument:
beforeRouteEnter (to, from, next) {
next(vm => {
// access to component instance via `vm`
})
}
This is why simply creating a stub or mock callback of next does not work in this case. I solved the problem by using the following parameter for next:
// mount the component
const wrapper = mount(Component, {});
// call the navigation guard manually
Component.beforeRouteEnter.call(wrapper.vm, undefined, undefined, (c) => c(wrapper.vm));
// await
await wrapper.vm.$nextTick();
I am using loadingController Ionic2.
`fetchNotificationListAferUserDataget(){
this.loader = this._loadingController.create({
content: "Please wait... Fetching online notifications",
dismissOnPageChange:true
});
this.loader.present();
this._userDataService.getNotificationList()
.subscribe(
(data) => {
this.loader.dismiss();
let status = data.status;
let returnedData = data.json();
console.log(status,returnedData)
if(data.status == 200){
if(returnedData.notifications.length > 0){
this.notifications = returnedData.notifications;
console.log(this.notifications);
this.loader = this._loadingController.create({
content: "Please wait... Fetching your purchased packages"
});
this.loader.present();
this._userDataService.getAllPackageByUser(this.userData.user_id)
.subscribe(
(data) => this.populateUserPackages(data),
(err) => this.showDataFetchErrorFromServer('Unable to fetch user packages')
)
}else if(returnedData.notifications.result == 0){
console.log('no notifications found');
}
}
},
(err) => {
this.showDataFetchErrorFromServer('Unable to fetch notifications')
}
);//end .subscribe
};`
But the problem I am facing is that loader appear and disappear automatically without my calling loader.dismiss();
Does anyone else facing same issue. Any solution for this.
EDIT: Full Function code included. loader dismiss immediately after loader.present(), without any error, but when I call this.loader.dismiss();, it gives me error because loader is already dismissed.
According to this issue, it is caused by triggering the loader.present() on the wrong life-cycle hook. I also had the same problem, where I had the loader loading on the ionViewDidLoad() hook. "The dom is not guaranteed to be ready in ionViewDidLoad and events are not guaranteed to be ready."
Try presenting the loader on the ionViewDidEnter() hook instead.
You need to use setTimeout() for this. Like:
setTimeout(() => {
this.loader.dismiss();
}, 1000);
Also, please do not use the same variable this.loader for creating 2 loaders. Just use a local variable like var loading = this._loadingController.create(). This could create problems in Loading API. In ionic 2 documentation here, it is mentioned:
Note that after the component is dismissed, it will not be usable anymore and another one must be created. This can be avoided by wrapping the creation and presentation of the component in a reusable function as shown in the usage section below.
I am creating a sample application using ember 1.0 and ember-data 1.0 Beta 2.0. with RESTAdapter to connect to backend server.
When I try to save a record, it always invoke failure handler at the first submission. But the record actually gets saved at backend without fail. From the server the response for submission contains the created entity set with id.
When I try to debug the code in developer tools, it actually goes through the code for route transition, but then it returns back to Add view before completing the transition. It seems to be some callbacks from jQuery global event handlers are causing the problem.
Here is the code I am using
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
this.modelFor('AddResource').save().then(function (resource) {
App.Router.router.transitionToRoute('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
Please help me to find out what is wrong with my code.
Thanks in advance
I think that you are receiving an error from App.Router.router.transitionToRoute('Resources'); invocation, try to update to the following:
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
var route = this;
this.modelFor('AddResource').save().then(function (resource) {
route.transitionTo('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
You should use transitionTo(someRoute) inside of a route, or transitionToRoute(someRoute) when inside of a controller