in the apollo-client how I may be able to log or intercept all the operations (queries and mutations) - apollo

For analytic purposes I'd like to keep track on the client side of all the graphql operations (including ie #client ones). I was unable to find appropriate options in the API and wonder if this may be doable on the apollo-client level or may I need to introduce some proxy to intercept the calls by my own?

A custom Apollo link is a way to go.
You can use apollo-link-logger in particular to log all operations to console.
Usage (from docs):
import apolloLogger from 'apollo-link-logger';
// ...
ApolloLink.from([
apolloLogger,
// ...
]);
Note: Place apolloLogger before other links.
Output example:

As the answer from Yuriy was exactly what I was looking for I marked is as accepted answer - Thanks!
Still for the record here is the code doing a job for me - I believe someone may find it useful, also it is worth to show it's simplicity.
It's worth noting that Apollo links are chainable - thus the argument to a link function are operation: Operation and forward: NextLink which is supposed to be called from our link implementation.
let analytics: Analytics; // this is Fabric.io Analytics to be provided by DI
const analyticsLink = new ApolloLink((
operation: Operation,
forward?: NextLink
) => {
const operationType = operation.query.definitions[0].operation;
return forward(operation)
.map((result: FetchResult) => {
try {
analytics.sendCustomEvent(`${operationType}.${operation.operationName}`);
} catch (e) {
console.error('analytics error', e);
}
return result;
});
});
as a bonus we can also catch errors (i.e. to leverage fabric.io crashlytics) by using apollo-link-error (handling of errors in Apollo is a bit more complex);
const analyticsErrorLink = onError((error: ErrorResponse) => {
try {
// it's worth to rethink what we wanna log here
const message = error.graphQLErrors ? error.graphQLErrors[0].message :
(error.networkError.name + ': ' + error.networkError.message);
analytics.sendNonFatalCrash('GraphQL error: ' + message);
} catch(e) {
console.error('cannot report error to analytics', e);
}
});
Finally to compose the links we should put our intercepting implementations at the beginning so we will be able to catch all the GraphQL operations including those marked with #client which are not reaching network link - in my case full link looks like:
ApolloLink.from([
analyticsErrorLink,
analyticsLink,
stateLink,
auth,
http])

Related

how to implement comparing two screenshots in one test with playwright

I am very new to playwright and i have a problem.
I am trying to implement comparing two screenshots (before and after) in one test.
this is what i want to achieve:
navigate to webpage
take screenshot (before.png)
do some stuff,state changes, etc
take screenshot (after.png)
compare before.png to after.png (if they are the same test should pass, otherwise test fails)
something like this:
test('compare screenshots', async ({ page }) => {
await page.goto('my website here');
const beforeImage = await page.screenshot({
path: `./screenshots/before.png`
})
//
// some state changes implemented here
//
const afterImage = await page.screenshot({
path: `./screenshots/after.png`
})
expect(beforeImage).toMatchSnapshot(afterImage)
});
but it does not work like this.
Any ideas/suggestions how can i achieve this?
Help would be greatly appreciated
You can do something like this:
test('compare screenshots', async ({ page }, testInfo)=>{
await page.goto(pageUrl);
const screenshotTarget = page.locator(scTarget);
await expect(screenshotTarget).toHaveScreenshot( `${testInfo.title}.png`);
//
// some state changes implemented here
//
await expect(screenshotTarget).toHaveScreenshot( `${testInfo.title}.png`);
});
I prefer to use the test titel for naming my screenshots but it should also work if you just enter the same name twice. Then if you run your tests without --update-snapshots they should fail if some visual changes happened.
The problem with Playwright's toHaveScreenshot and toMatchSnapshot is that they're a bit over-engineered and will only compare a current screenshot to a screenshot from a previous test run. If you want to compare two screenshots that you have as Buffers in memory, you can use the getComparator method that Playwright uses behind the scenes:
import { getComparator } from 'playwright-core/lib/utils';
await page.goto('my website here');
const beforeImage = await page.screenshot({
path: `./screenshots/before.png`
});
//
// some state changes implemented here
//
const afterImage = await page.screenshot({
path: `./screenshots/after.png`
});
const comparator = getComparator('image/png');
expect(comparator(beforeImage, afterImage)).toBeNull();
The advantage of using getComparator is that it fuzzy matches, and you can set the threshold of how many pixels are allowed to be different. If you just want to check that the PNGs are exactly identical, a dead simple method to check for equality between the two screenshots is:
expect(Buffer.compare(beforeImage, afterImage)).toEqual(0)
Beware though - this simpler method is flakey and sensitive to a single pixel difference in rendering (such as if any animations/transitions are not completed or if there are differences in anti-aliasing).

Google Actions SDK "Error: Unauthorized, Your client does not have permission to the requested URL" caused by use of conv.user.storage

I'm trying to convert an existing Alexa app to Google Actions wherein I need to implement session and persistent data values. My understanding from https://developers.google.com/assistant/conversational/df-asdk/save-data is that conv.data and conv.user.storage are intended for this purpose. However, making any attempt to assign values to either results in the error "Error: Unauthorized, Your client does not have permission to the requested URL", and also a reference to the offending key which points to this in the firebase console log: https://us-central1-hello-world-e37ec.cloudfunctions.net/cf-p7ROQlBMjQId9Cws6XdJBA-name. Similar issues here in stackoverflow seem to indicate that I need to grant the appropriate function to all users, but I don't know which function is being called. I'm new to Google Actions, so apologies if I'm overlooking something obvious. Code is very similar to the example offered on google's doc.
const {conversation} = require('#assistant/conversation');
const functions = require('firebase-functions');
const app = conversation();
...
app.handle('status', async conv => {
conv.overwrite = false;
if (conv.user.verificationStatus === 'VERIFIED') {
conv.user.storage = {};
conv.user.storage.sum = 69;
conv.add(`Alright, I'll store that for next time. See you then.`);
} else {
conv.add(`I can't save that right now, but we can add ` +
`new numbers next time!`);
}
});
I found the answer for this issue. Appears that I was not in the correct area of documentation for the "conversation" object/app. Correct method is described here: https://developers.google.com/assistant/conversational/webhooks#read_and_write_storage.
Using my example
app.handle('status', async conv => {
conv.overwrite = false;
if (conv.user.verificationStatus === 'VERIFIED') {
conv.session.params.sum = 69; //within session
conv.user.params.sum = 100; //across sessions
conv.add(`Alright, I'll store that for next time. See you then.`);
} else {
conv.add(`I can't save that right now, but we can add ` +
`new numbers next time!`);
}
});

Loopback include unrelated lists

In Loopback it is easy to include relational objects when querying for data. For example, one can include all the comments that belong to a blog post in a single call using the include filter.
But in my case I want to get data that doesn't have a relation.
I have a User Detail page. On that page a user can choose a username and there's also a dropdown list where a user can choose from what country he is.
So from the client side I do something like:
Country.find().$promise.then(function(countryData) {
$scope.countries = countryData;
});
Player.find().$promise.then(function(playerData) {
$scope.player = playerData;
}
But what if I get more lists that I want to fill? Like, city, state, colors etc.
Then I'd have to make a lot of separate calls.
Is there a way to include all this data in one call, eventhough they have no relation? Something like this:
Player.find({ filter: { include: ["countries", "colors"] } }).$promise.then(function(data) {
// some stuff
}
You may want to try using the Where filter as documented here
An example of this for querying two specific things would be:
Post.find({where: {and: [{title: 'My Post'}, {content: 'Hello'}]}},
function (err, posts) {
...
});
You could create a remote method on one of your models that makes the calls internally and packages them back up for you.
Use some promise library if not using ES6 to wait for all and then return
Model.getAll = function(next) {
var promises = [];
promises.push(Model.app.models.Country.find());
promises.push(Model.app.models.Player.find());
promises.push(Model.app.models.Color.find());
Promise.all(promises)
.then(function(results) {
next(results);
});
}
/**
Register your remote method here
*/
You could create a remote method on one of your models that makes the calls internally and packages them back up for you.
Use some promise library if not using ES6 to wait for all and then return
Model.getAll = function(next) {
var promises = [];
promises.push(Model.app.models.Country.find());
promises.push(Model.app.models.Player.find());
promises.push(Model.app.models.Color.find());
Promise.all(promises)
.then(function(results) {
next(results);
});
}
/**
Register your remote method here
*/
I have problem and try with this solution but i get error "Failed with multiple errors, see details for more information.". It seems like there is bug on Loopback while using promise.all

Pre-routing with querystrings with Express in Node JS

I'm trying to use express to parse the querystring in case certain parameters are set and execute a little piece of code, before the actual routing is happening. The use-case is to grab a certain value, that could be set, independant of what link is being used. I use express' functionality to pass the stuff to the next possible rule using next().
So far, I tried - at the very top of all the app.get/post-rule-block:
app.get('[?&]something=([^&#]*)', function(req, res, next) {
var somethingID = req.params.something;
// Below line is just there to illustrate that it's working. Actual code will do something real, of course.
console.log("Something: "+somethingID);
next();
})
app.get('/', site.index);
and also:
app.param('something', function(req, res, next) {
var somethingID = req.params.something;
console.log("Something: "+somethingID);
next();
})
app.get('/', site.index);
Example of what should be triggered:
URL: www.example.com/?something=10239
URL: www.example.com/superpage/?something=10239
URL: www.example.com/minisite/?anything=10&something=10239
Unfortunately, none of my solutions actually worked, and all that happens is, that the next matching rule is triggered, but the little function above is never executed. Anybody have an idea, of how this can be done?
EDIT: I do understand, that the param-example wasn't working, as I'm not using said parameter within any other routing-rule afterwards, and it would only be triggered then.
I also do understand, that logic implies, that Express ignores the querystring and it is normally parsed within a function after the routing already happened. But as mentioned, I need this to be "route-agnostic" and work with any of the URL's that are processed within this application.
express does not allow you to route based on query strings. You could add some middleware which performs some operation if the relevant parameter is present;
app.use(function (req, res, next) {
if (req.query.something) {
// Do something; call next() when done.
} else {
next();
}
});
app.get('/someroute', function (req, res, next) {
// Assume your query params have been processed
});
Ok, there is quite a logical flaw in here. Routing only uses the URL, and ignores the querystring.
The (or better "A") solution is actually this:
app.get('*', function(req, res, next) {
if (req.query.something) {
console.log("Something: "+req.query.something);
};
next();
})
Explanation: As Express is ignoring the querystring for the routing, the only regular expression matching all URL's is "*". Once that is triggered, I can check if said querystring is existing, do my logic and continue the routing matching the next rule by using "next()".
And yes: facepalm

Call multiple webservices from play 2

I am a play2.0-Scala-beginner and have to call several Webservices to generate a HTML page.
After reading the The Play WS API page and a very interesting article from Sadek Drobi I am still unsure what's the best way to accomplish this.
The article shows some code snippets which I don't fully understand as a Play beginner.
Figure 2 on page 4:
val response: Either[Response,Response] =
WS.url("http://someservice.com/post/123/comments").focusOnOk
val responseOrUndesired: Either[Result,Response] = response.left.map {
case Status(4,0,4) => NotFound
case Status(4,0,3) => NotAuthorized
case _ => InternalServerError
}
val comments: Either[Result,List[Comment]] =
responseOrUndesired.right.map(r => r.json.as[List[Comment]])
// in the controller
comment.fold(identity, cs => Ok(html.showComments(cs)))
What does the last line with the fold do? Should comment be comments? Haven't I group the last statement in an Async block?
Figure 4 shows how to combine several IO calls with a single for-expression:
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
}
// in the controller
def showInfo(...) = Action { rq =>
Async {
actorInfo(...).map(info => Ok(info))
}
}
How can I use this snippet? (I am a bit confused by the extra-} after the for-expression.)
Should I write something like this?
var actorInfo = for { // Model
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield Json.obj(
"profile" -> profile,
"events" -> events,
"articles" -> articles )
def showInfo = Action { rq => // Controller
Async {
actorInfo.map(info => Ok(info))
}
}
What's the best way to combine the snippets from figure 2 and 4 (error handling + composition of IO non-blocking calls)? (f.ex. I want to produce a Error 404 status code if any of the called webservice produce an Error 404).
Maybe someone knows a complete example of calling webservices in the play framework (cannot find an example in the play Sample applications or anywhere else).
I have to say that the article is wrong in the example you show in Figure 2. The method focusOnOk does not exist in Play 2.0. I assume the author of the article used a pre-release version of Play 2 then.
Regarding comment, yes it should be comments. The fold in the statement is operating on an Either. It takes 2 functions as parameters. The first is a function to apply if it is a left value. The second is a function to apply if it is a right value. A more detailed explanation can be found here: http://daily-scala.blogspot.com/2009/11/either.html
So what the line does is. If I have a left value (which meant I got an undesired response), apply the built-in identity function which just gives you back the value. If it has a right value (which means I got an OK response), make a new result that shows the comments somehow.
Regarding Async, it's not actually asynchronous. focusOnOk is a blocking function (a remnant from the old Java days of Play 1.x). But remember, that's not valid Play 2 code.
As for Figure 4, the trailing } is actually because it's a partial alternative of what's in Figure 3. Instead of the numerous promise flatMaps. You can do a for comprehension instead. Also, I think it should be userInfo(...).map instead of actorInfo(...).map.
The Play documentation you linked to actually already shows you a full example.
def feedTitle(feedUrl: String) = Action {
Async {
WS.url(feedUrl).get().map { response =>
Ok("Feed title: " + (response.json \ "title").as[String])
}
}
}
will get whatever is at feedUrl, and you map it to do something with the response which has a status field you can check to see if it was a 404 or something else.
To that end, the Figure 3 and 4 of your linked article should give you a starting point. So you'd have something like,
def getInfo(...) : Promise[String] = {
val profilePromise = WS.url(...).get()
val attachedEventsPromise = WS.url(...).get()
val topArticlesPromise = WS.url(...).get()
for {
profile <- profilePromise
events <- attachedEventsPromise
articles <- topArticlesPromise
} yield {
// or return whatever you want
// remember to change String to something else in the return type
profile.name
}
}
def showInfo(...) = Action { rq =>
Async {
getInfo(...).map { info =>
// convert your info to a Result
Ok(info)
}
}
}