I want to integrate Google mobile friendly and desktop friendly api with PageSpeed Insights API V5. But I'm unable to differentiate Audit section. I tried too scenario's for differntiate but I couldn't.
How to differentiate Passed Audits, Diagnostics and Opportunities in PageSpeed Insights API V5?
Below is the code which GoogleChrome lighthouse uses to differentiate Opportunities, Diagnostics and Passed audits which you can find at below github link.
// Opportunities
const opportunityAudits = category.auditRefs
.filter(audit => audit.group === 'load-opportunities' && !Util.showAsPassed(audit.result))
.sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
// Diagnostics
const diagnosticAudits = category.auditRefs
.filter(audit => audit.group === 'diagnostics' && !Util.showAsPassed(audit.result))
.sort((a, b) => {
const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
return scoreA - scoreB;
});
// Passed audits
const passedAudits = category.auditRefs
.filter(audit => (audit.group === 'load-opportunities' || audit.group === 'diagnostics') &&
Util.showAsPassed(audit.result));
Reference : https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/html/renderer/performance-category-renderer.js
In above code the Util.showAsPassed() method has been specified as below.
const PASS_THRESHOLD = 0.9;
const RATINGS = {
PASS: {label: 'pass', minScore: PASS_THRESHOLD},
AVERAGE: {label: 'average', minScore: 0.5},
FAIL: {label: 'fail'},
ERROR: {label: 'error'},
};
static showAsPassed(audit) {
switch (audit.scoreDisplayMode) {
case 'manual':
case 'notApplicable':
return true;
case 'error':
case 'informative':
return false;
case 'numeric':
case 'binary':
default:
return Number(audit.score) >= RATINGS.PASS.minScore;
}
}
Reference : https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/report/html/renderer/util.js
Thanks
** Opportunities:** There is no compiled set of opportunities available in the response but we can loop through lighthouseResult and for each json inside the lighthouseReslt, take out the results with type=opportunity and it should also have details.
response = requests.get('https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url='+<url>+'&strategy='+<desktop or mobile>+'&key='+<api_key>)
js = response.json()
print ("Score: ",js['lighthouseResult']['categories']['performance']['score']*100)
k = []
for m in js['lighthouseResult'] :
try :
for i in js['lighthouseResult'][m] :
k.append([js['lighthouseResult'][m][i]['details'],js['lighthouseResult'][m][i]['title']])
except :
pass
final_opportunities = []
print (len(k))
for i in k :
if 'overallSavingsMs' in list(i[0].keys()) :
print (i[1],i[0]['overallSavingsMs'])
final_opportunities.append([i[1] , i[0]['overallSavingsMs']])
Audit results can be found in :
lighthouseResult.audits
The aggregate performance score : response.lighthouseResult.categories.performance.score
For a python implementation, you could refer to the following github repo:
https://github.com/amartya-dev/PageSpeedAPI
Related
Is there a way to query the results to show only data that has been published and is not in draft state? I looked in the documentation and didn't quite find it.
This is what I currently have:
export const getAllPages = async (context?) => {
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const pages = await client.getEntries({
content_type: "page",
include: 10,
"fields.slug[in]": `/${context.join().replace(",", "/")}`,
});
return pages?.items?.map((item) => {
const fields = item.fields;
return {
title: fields["title"],
};
});
};
You can detect that the entries you get are in Published state:
function isPublished(entity) {
return !!entity.sys.publishedVersion &&
entity.sys.version == entity.sys.publishedVersion + 1
}
In your case, I would look for both Published and Changed:
function isPublishedChanged(entity) {
return !!entity.sys.publishedVersion &&
entity.sys.version >= entity.sys.publishedVersion + 1
}
Check the documentation:
https://www.contentful.com/developers/docs/tutorials/general/determine-entry-asset-state/
To get only the published data you will need to use the Content Delivery API token. If you use the Content Preview API Token, you will receive both the published and draft entries.
You can read more about it here: https://www.contentful.com/developers/docs/references/content-delivery-api/
If using the Content Delivery API you need to filter on the sys.revision attribute for each item. A published item should have its revision attribute set to greater than 0.
const publishedItems = data.items.filter(item => item.sys.revision > 0)
We have large code with lot of function called in series . For performance improvement we want to measure the time between to specific point.
Step by step we want to make improvement. I was thinking of making user of perf_hook
Our best use case will be using as below , as per the lib
const { PerformanceObserver, performance } = require('perf_hooks');
const obs = new PerformanceObserver((items) => {
console.log(items.getEntries()[0].duration);
performance.clearMarks();
});
obs.observe({ type: 'measure' });
performance.measure('Start to Now');
performance.mark('A');
doSomeLongRunningProcess(() => {
performance.measure('A to Now', 'A');
performance.mark('B');
performance.measure('A to B', 'A', 'B');
});
I looked and searched in lib as well as web , did not find any env variable to enable or disable the perf_hook, so enable and disable the code in PROD stage.
playing around the code .
Just putting the declaration based on condition
let obs = null;
//ACTIVATE or DEACTIVATE
if( true || false) {
obs = new PerformanceObserver((items) => {
console.log(items.getEntries()[0].duration);
performance.clearMarks();
});
obs.observe({ type: 'measure' });
}
I've looked everywhere and tried all I could think of but found nothing, everything seemed to fail.
One bit of code I've used before that failed:
Message.author.send({ embeds: [AttachmentEmbed] }).then(Msg => {
var Collector = Msg.channel.createMessageCollector({ MessageFilter, max: 1, time: 300000 });
Collector.on(`collect`, Collected => {
if (Collected.content.toLowerCase() !== `cancel`) {
console.log([Collected.attachments.values()].length);
if ([Collected.attachments.values()].length > 0) {
var Attachment = [Collected.attachments.values()];
var AttachmentType = `Image`;
PostApproval(false, Mode, Title, Description, Pricing, Contact, Attachment[0], AttachmentType);
} else if (Collected.content.startsWith(`https://` || `http://`) && !Collected.content.startsWith(`https://cdn.discordapp.com/attachments/`)) {
var Attachment = Collected.content.split(/[ ]+/)[0];
var AttachmentType = `Link`;
PostApproval(false, Mode, Title, Description, Pricing, Contact, Attachment, AttachmentType);
console.log(Attachment)
} else if (Collected.content.startsWith(`https://cdn.discordapp.com/attachments/`)) {
var Attachment = Collected.content.split(/[ ]+/)[0];
var AttachmentType = `ImageLink`;
PostApproval(false, Mode, Title, Description, Pricing, Contact, Attachment, AttachmentType);
console.log(Attachment)
}
[Collected.attachments.values()].length will always be 1. Why? Well you have these 2 possibilities:
[ [] ] //length 1
[ [someMessageAttachment] ] //length 1
The proper way to check is using the spread operator (...)
[...(Collected.attachments.values())].length //returns amount of attachments in the message
I'm using React-Slick to render <Report /> components in a carousel. I would like to sync each <Report />'s reportId with query params.
For example, a user would be able to see a specific report by going to myapp.com/reports?id=1 and it would take them to that specific "slide".
The problem I'm having is that the report data is being loaded before the slides are initialized. I can't find any good examples of react-slick's onInit or onReInit.
Instead of using onInit or onReInit, I just utilized the initialSlide setting and used componentDidUpdate().
componentDidUpdate = (prevProps, prevState) => {
const queryParams = qs.parse(this.props.location.search)
if (prevProps.reports !== this.props.reports) {
const sortedReports = this.sortReportsByDate(this.props.reports)
this.setSlideIndex(sortedReports.findIndex(i => i.id === parseInt(queryParams.reportId, 10)))
this.setQueryParams({
reportId: this.sortReportsByDate(this.props.reports)[this.state.slideIndex].id
})
}
if (prevState.slideIndex !== this.state.slideIndex) {
this.setQueryParams({
reportId: this.sortReportsByDate(this.props.reports)[this.state.slideIndex].id
})
}
}
And the settings:
const settings = {
...
initialSlide: reportsSortedByDate.findIndex(i => i.id === parseInt(queryParams.reportId, 10))
...
}
I hope someone finds this useful!
I found an important security fault in my meteor app regarding subscriptions (maybe methods are also affected by this).
Even though I use the check package and check() assuring that the correct parameters data types are received inside the publication, I have realised that if a user maliciously subscribes to that subscription with wrong parameter data types it is affecting all other users that are using the same subscription because the meteor server is not running the publication while the malicious user is using incorrect parameters.
How can I prevent this?
Packages used:
aldeed:collection2-core#2.0.1
audit-argument-checks#1.0.7
mdg:validated-method
and npm
import { check, Match } from 'meteor/check';
Server side:
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: String,
section: String,
});
return Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
});
Client side:
// in the template:
Meteor.subscribe('postersPub', { size: 'large', section: 'movies' });
// Malicious user in the browser console:
Meteor.subscribe('postersPub', { size: undefined, section: '' });
Problem: The malicious user subscription is preventing all other users of getting answer from their postersPub subscriptions.
Extra note: I've also tried wrapping the check block AND the whole publication with a try catch and it doesn't change the effect. The error disappears from the server console, but the other users keep being affected and not getting data from the subscription that the malicious user is affecting.
Check method and empty strings
There is one thing to know about check and strings which is, that it accepts empty strings like '' which you basically showed in your malicious example.
Without guarantee to solve your publication issue I can at least suggest you to modify your check code and include a check for non-empty Strings.
A possible approach could be:
import { check, Match } from 'meteor/check';
const nonEmptyString = Match.Where(str => typeof str === 'string' && str.length > 0);
which then can be used in check like so:
check(params, {
size: nonEmptyString,
section: nonEmptyString,
});
Even more strict checks
You may be even stricter with accepted parameters and reduce them to a subset of valid entries. For example:
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
Note, that this also helps you to avoid query logic based on the parameter. You can change the following code
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
to
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
because the method does anyway accept only one of large or small as parameters.
Fallback on undefined cursors in publications
Another pattern that can support you preventing publication errors is to call this.ready() if the collection returned no cursor (for whatever reason, better is to write good tests to prevent you from these cases).
const posters = Posters.find({
section: params.section,
size: params.size === 'large' ? 'large' : 'small',
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
Combined code example
Applying all of the above mentioned pattern would make your function look like this:
import { check, Match } from 'meteor/check';
const sizes = ['large', 'small'];
const nonEmptyString = str => typeof str === 'string' && str.length > 0;
const validSize = str => nonEmptyString(str) && sizes.indexOf( str) > -1;
Meteor.publish('postersPub', function postersPub(params) {
check(params, {
size: Match.Where(validSize),
section: Match.Where(nonEmptyString),
});
const posters = Posters.find({
section: params.section,
size: params.size,
}, {
// fields: { ... }
// sort: { ... }
});
// if we have a cursor with count
if (posters && posters.count && posters.count() >= 0)
return posters;
// else signal the subscription
// that we are ready
this.ready();
});
Summary
I for myself found that with good check matches and this.ready() the problems with publications have been reduced to a minimum in my applications.