I'm using a workbox and trying to cache requests something like this:
this.serviceWorker.addEventListener('fetch', (event) => {
const request = event.request;
event.respondWith(
caches.open('cache name')
.then(cache => cache.match(request))
.then(async (cachedResponse) => {
// todo something
How Can I cancel event.request and send my custom request?
Thanks for any helping.
You may alter or completely change the out-going request like so:
this.serviceWorker.addEventListener('fetch', (event) => {
const request = event.request;
return event.respondWith(
caches.open('cache name')
.then(cache => cache.match(request))
.then(async (cachedResponse) => {
// create a new request, fetchi it, and return the result
return fetch(new Request(request.url ....))
})
Pay attention to the return statement I added to the third line of code :)
More info about constructing the new request: https://stackoverflow.com/a/35421858/5038943
Related
I created a service worker for a payment manager. When I checked the server is installed but I do not see the payment methods installed on the browser.
i added the event install fetch and load but i can't show the payment methods register in browser..
Any help please
My installer is
navigator.serviceWorker.register('sw.js').then(() => {
return navigator.serviceWorker.ready;
}).then((registration) => {
if (!registration.paymentManager) {
console.log('No payment handler capability in this browser. Is chrome://flags/#service-worker-payment-apps enabled?');
return;
}
if (!registration.paymentManager.instruments) {
return;
}
registration.paymentManager.instruments
.set('instrument-key', {
name: 'Chrome uses name and icon from the web app manifest',
enabledMethods: ['https://...'],
method: 'https://...',
})
.then(() => {
registration.paymentManager.instruments.get('instrument-key').then((instrument) => {
}).catch(...
})
My server worker
let payment_request_event;
let resolver;
let client;
const addResourcesToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};
self.addEventListener('install', event => {
event.waitUntil(
addResourcesToCache([
"/",
"/index.html",
])
);
console.log(`SW: Event fired: ${event.type}`);
console.dir(event);
});
self.addEventListener('activate', event => {
console.dir(event);
});
self.addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});
self.addEventListener('canmakepayment', e => {
console.log('canmakepayment', e);
e.respondWith(true);
});
self.addEventListener('paymentrequest', async e => {
console.log(e)
payment_request_event = e;
e.respondWith(resolver.promise);
// Open the checkout page.
try {
// Open the window and preserve the client
client = await e.openWindow('https://...html');
...
I am currently using #react-native-community/cameraroll currently and i am trying to get images and albums on my ios device.
The following is the code I tried
CameraRoll.getPhotos({})
.then((res) => {
console.log("result 1", res)
}).catch((error) => { })
CameraRoll.getAlbums({})
.then((res) => {
console.log("result 2", res)
})
.catch((error) => {
console.log(error);
});
result 1 and result 2 give the following result
result 1 {"edges": [], "page_info": {"has_next_page": false}}
result 2 []
Any help would be appreciated. The same code works well on Android.
I tried with a lot of combinations and the one below worked for the ios device -
CameraRoll.getPhotos({ first: 5000, assetType: 'Photos' })
.then((result) => {
const tempBuckets = [];
var edgesArr = result.edges;
}).catch((error) => { })
The result here contains an edges array that has all the images and their respective properties.
The count '5000' is an arbitrary number i have used, without which the result array obtained was empty.
the solution is to add permission before request like
async function hasAndroidPermission() {
const permission =
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
const hasPermission = await PermissionsAndroid.check(permission);
if (hasPermission) {
return true;
}
const status = await PermissionsAndroid.request(permission);
return status === 'granted';
}
and use like this
const Gallery = async () =>{
if (Platform.OS === "android" && !(await hasAndroidPermission())) {
return;
}
CameraRoll.getAlbums({assetType: "Photos", albumType: "All"})
.then((r:any) => {
console.log("images-->",r)
})
.catch((err:any) => {
//Error Loading Images
console.log("err->",err)
});
}
I'm facing a "runtime application timed out" error whenever I try to filter TaskQueues. Here is the complete scenario.
On the twilio flex contact pad, I have to show cumulative count of pending tasks from all the task queues, that the agent belongs to. For this, I have written a twilio function which takes the worker skills as input and filters TaskQueues based on skills supplied, and count the pending tasks. I followed the twilio documentation provided here.
I tried different ways to write the expression, but all are eventually resulting in time out error.
The worker attributes are defined as below
{
"routing": {
"skills": [
"German",
"English",
"French"
],
"domains": [
"ABC"
],
"categories": [
"XYZ"
],
"levels": {
"XYZ": 34
},
"platforms": [
"Platform1"
],
"provider": "Provider_1"
},
"full_name": "XXXXXXX",
"image_url": "https:\/\/www.avatar.co\/avatar\/91f0e496?d=mp",
"roles": [
"admin",
"wfo.full_access"
],
"contact_uri": "client:XXX.YYYY",
"disabled_skills": {
"skills": [],
"levels": {}
},
"email": "test#email.com"
}
Below the code snippet to filter the Queues.
const TokenValidator = require('twilio-flex-token-validator').functionValidator;
const WORKSPACE_ID = 'WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const workerSkills = ['English','French'];
console.log('SKILLS IN FUNC : ' + `routing.skills IN [${workerSkills}]`);
const getQueues = (client) => {
return new Promise ((resolve, reject) => {
client.taskrouter.v1.workspaces(WORKSPACE_ID)
.taskQueues
.list({evaluateWorkerAttributes:'{"routing.skills" : "${workerSkills}" }',
//.list({evaluateWorkerAttributes:"routing.skills HAS 'French'",
limit: 1000
})
.then(taskQueues => resolve(taskQueues))
});
}
const getQueueRealTimeStatistics = (client, queueID) => {
return new Promise ((resolve, reject) => {
client.taskrouter.v1.workspaces(WORKSPACE_ID)
.taskQueues(queueID)
.realTimeStatistics()
.fetch()
.then(realTimeStatistics => resolve(realTimeStatistics))
});
}
exports.handler = function(context, event, callback) {
const client = context.getTwilioClient();
let pendingTasks = 0;
const promises = [];
const response = new Twilio.Response();
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'OPTIONS POST GET');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
response.appendHeader('Content-Type', 'application/json');
getQueues(client).then(res => res.forEach(x => {
promises.push(getQueueRealTimeStatistics(client, x.sid));
console.log('queueID : ' + x.sid);
})).then(x => {
Promise.all(promises).then(res => {
res.forEach(y => {
pendingTasks = pendingTasks+ parseInt(y.tasksByStatus.pending,10);
})
//response.setBody(pendingTasks);
response.setBody(pendingTasks + parseInt(res, 10));
})
.then(x => callback(null, response))
.catch(err => {
console.log(err.message);
response.appendHeader('Content-Type', 'plain/text');
response.setBody(err.message);
response.setStatusCode(500);
// If there's an error, send an error response
// Keep using the response object for CORS purposes
callback(null, response);
});
})
};
Can someone help to resolve this issue. Thanks in advance.
Twilio developer evangelist here.
It looks like your function is written mostly correctly and should return. There's two things I can think of that might be going wrong.
It really is timing out
I'm not sure how many queues you have, but getting all the queues and then getting all the statistics for each queue could just be pushing your Function over the 10 second limit for Function execution. If that is the case, then may have to break up the Function so that it can run within the time. Are you able to run this locally (using the Twilio Serverless Toolkit) and it succeed?
There's an error that is not being caught and the promises don't resolve
Your code looks right to me, but the way you wrap the results of API calls in a new promise means that if there is an error the wrapper promise never resolves or rejects. This would mean that the Function hangs waiting for a result until the timeout occurs.
Thing is, you don't need to wrap the API calls in a new Promise, they already return Promises.
I would try rewriting it like this, avoiding new Promise wrappers and then seeing if there is an error that you aren't catching. You can also avoid pushing promises into an array and jump straight into the Promise.all too:
const TokenValidator = require("twilio-flex-token-validator").functionValidator;
const WORKSPACE_ID = "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
const workerSkills = ["English", "French"];
console.log("SKILLS IN FUNC : " + `routing.skills IN [${workerSkills}]`);
const getQueues = (client) => {
return client.taskrouter.v1.workspaces(WORKSPACE_ID).taskQueues.list({
evaluateWorkerAttributes: '{"routing.skills" : "${workerSkills}" }',
//.list({evaluateWorkerAttributes:"routing.skills HAS 'French'",
limit: 1000,
});
};
const getQueueRealTimeStatistics = (client, queueID) => {
return client.taskrouter.v1
.workspaces(WORKSPACE_ID)
.taskQueues(queueID)
.realTimeStatistics()
.fetch();
};
exports.handler = function (context, event, callback) {
const client = context.getTwilioClient();
let pendingTasks = 0;
const response = new Twilio.Response();
response.appendHeader("Access-Control-Allow-Origin", "*");
response.appendHeader("Access-Control-Allow-Methods", "OPTIONS POST GET");
response.appendHeader("Access-Control-Allow-Headers", "Content-Type");
response.appendHeader("Content-Type", "application/json");
getQueues(client)
.then((queues) => {
return Promise.all(
queues.map((queue) => {
console.log("queueID : " + queue.sid);
return getQueueRealTimeStatistics(client, queue.sid);
})
);
})
.then((queueStats) => {
queueStats.forEach((queueStat) => {
pendingTasks = pendingTasks + parseInt(queueStat.tasksByStatus.pending, 10);
});
response.setBody({ pendingTasks });
})
.then(() => callback(null, response))
.catch((err) => {
console.log(err.message);
response.appendHeader("Content-Type", "plain/text");
response.setBody(err.message);
response.setStatusCode(500);
// If there's an error, send an error response
// Keep using the response object for CORS purposes
callback(null, response);
});
};
Doing it this way cuts down some of the nesting too. Let me know if it helps.
Here is what I tried but it's not working.
This guy is responsible for creating a composition after the meeting is ended.
app.post('/api/endMeeting', (req, res) => {
const roomSid = req.body.roomSid;
userEmail = req.body.userEmail;
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
client.video.rooms(roomSid).update({ status: 'completed' });
client.video.compositions.create({
roomSid: roomSid,
audioSources: '*',
videoLayout: {
grid : {
video_sources: ['*']
}
},
statusCallback: `${process.env.REACT_APP_BASE_URL}/api/getMeeting`,
statusCallbackMethod: 'POST',
format: 'mp4'
}).then(() => {
// sendRecordingEmail(composition.sid, userEmail);
res.status(200).send({
message: 'success'
});
}).catch(err => {
res.status(500).send({
message: err.message
});
});
});
And this guy will send the download link of the composition to the participant when it's available.
app.post('/api/getMeeting', (req, res) => {
if (req.query.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.query.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = request(response.data.redirect_to);
sendRecordingEmail(requestUrl, userEmail);
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
}
});
I can confirm that the composition is created exactly in the Twilio console.
But it seems the status callback guy is not working and I can see the below issue.
It seems I made mistakes in using the status callback.
Please let me know what is the problem and how I can solve this.
Thank you.
Thank you very much for #philnash's help in solving this problem.👍
I solved the above issue and I can get the download link of the composition for now.
The problem was in the status callback function and I should use req.body instead of req.query because of the status callback method. (It's POST on my code.)
Here is the code that is fixed.
app.post('/api/getMeeting', (req, res) => {
if (req.body.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.body.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = response.body.redirect_to; // Getting the redirect link that user can download composition
sendRecordingEmail(requestUrl, userEmail); // Send URL via email to the user
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
} else {
res.status(204).send('compositioin is not available');
}
});
Everything seems to be right and the files are being cached but it just doesn't work offline. Am I missing something obvious?
the cache.addAll did not want to work with my const FILES_TO_CACHE but do work when I put them in directly. Thus the repeated code.
Here is my service worker file:
const FILES_TO_CACHE = [
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
];
const CACHE_NAME = "static-cache-v2";
const DATA_CACHE_NAME = "data-cache-v1";
// install
self.addEventListener("install", function(evt) {
evt.waitUntil(
caches.open(CACHE_NAME).then(cache => {
console.log("Your files were pre-cached successfully!");
return cache.addAll([
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
]);
})
);
self.skipWaiting();
});
// activate
self.addEventListener("activate", function(evt) {
console.log("activated");
evt.waitUntil(
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
console.log("Removing old cache data", key);
return caches.delete(key);
}
})
).catch(err => console.log(err));
})
);
self.clients.claim();
});
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
evt.respondWith(
caches
.open(FILES_TO_CACHE)
.then(cache => {
return fetch(evt.request)
.then(response => {
// If the response was good, clone it and store it in the cache.
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
})
.catch(err => {
// Network request failed, try to get it from the cache.
return cache.match(evt.request);
});
})
.catch(err => console.log(err))
);
return;
}
});
link in html:
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js").then(function() {
console.log("Service Worker Registered");
});
}
</script>
I also have my manifest linked in the HTML file.
Thank you in advance for any help you can provide!
If you look at the last line of code here:
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
you see that there's a very simple mistake – your Service Worker is ONLY responding to requests that start with "/api/". If they don't, the SW doesn't touch them. Thus only "/api/" calls work offline (which doesn't make any sense :-), apis being mostly dynamic, right?).
(It is possible that there's another bug in the code of course, but this is a good point to start making changes.)