I'm switching from sw-toolbox to Workbox and I can't figure out how to use setDefaultHandler().
If I try (as stated in the documentation linked above):
workboxSW.router.setDefaultHandler({
handler: new workbox.runtimeCaching.CacheFirst()
});
I get an error that runtimeCaching is undefined:
Uncaught ReferenceError: router is not defined
So.. how do I use it and configure it in a way similar to how I could configure sw-toolbox:
toolbox.options.cache = {
name: "default",
maxEntries: 128,
maxAgeSeconds: (60*60*24), // 24hrs
};
toolbox.router.default = toolbox.cacheFirst;
I would like to be able to do something like this:
workboxSW.router.setDefaultHandler({
handler: workboxSW.strategies.cacheFirst({
cacheName: 'default',
cacheExpiration: {
maxEntries: 128,
},
cacheableResponse: {statuses: [0, 200]},
})
});
..which doesn't throw compile errors but when I use it I get this:
Uncaught (in promise) TypeError: Request method 'POST' is unsupported
..and my Cache Storage for 'default' remains empty..?
Since my edits for Jeff's first solution were rejected I'll just go ahead and submit an answer myself.
Jeff's sample came close. He suggested:
You could check for the request type in the default handler, and only
apply the cache-first strategy to GET requests:
workboxSW.router.setDefaultHandler({
handler: (args) => {
if (args.event.request.method === 'GET') {
return workboxSW.strategies.cacheFirst(args);
}
return fetch(args.event.request);
},
});
It's the right approach but the example code he provided didn't work. The handler argument needs a handler, not a strategy. Luckily, strategies have exactly one (public) method, called "handle".
So I modified his code a little; First, I create a strategy called defaultStrategy with all the options I need. Then, in the setDefaultHandler call, I return defaultStrategy.handle(args) instead of the CacheFirst constructor. That's it!
// Register 'default'
var defaultStrategy = workboxSW.strategies.cacheFirst({
cacheName: "default",
cacheExpiration: {
maxEntries: 128,
// more options..
},
cacheableResponse: {statuses: [0, 200]},
});
workboxSW.router.setDefaultHandler({
handler: (args) => {
if (args.event.request.method === 'GET') {
return defaultStrategy.handle(args);
}
return fetch(args.event.request);
},
});
UPDATE: Workbox v3
As I pointed out in the comments below, the above code doesn't work with Workbox v3. Use this instead:
// Register 'default'
var defaultStrategy = workbox.strategies.cacheFirst ({
cacheName: "your.cache.name",
plugins: [
new workbox.expiration.Plugin({
maxEntries: 128,
maxAgeSeconds: 7 * 24 * 60 * 60, // 1 week
purgeOnQuotaError: true, // Opt-in to automatic cleanup
}),
new workbox.cacheableResponse.Plugin({
statuses: [0, 200] // for opague requests
}),
],
});
workbox.routing.setDefaultHandler(
(args) => {
if (args.event.request.method === 'GET') {
return defaultStrategy.handle(args); // use default strategy
}
return fetch(args.event.request);
}
);
workboxSW.router.setDefaultHandler({
handler: workboxSW.strategies.cacheFirst({...})
});
is the right syntax in general.
I believe that you're seeing
Uncaught (in promise) TypeError: Request method 'POST' is unsupported
because the default handler is triggered for all HTTP requests that don't match any explicit route, including HTTP POST requests. But a HTTP POST request can't be used with the Cache Storage API, and an exception similar to what you're seeing will be thrown when the cache-first strategy attempts to store the request/response pair in the cache.
In this particular case, when you know that your web app is going to make HTTP POST requests, you could take one of two approaches.
You could check for the request type in the default handler, and only apply the cache-first strategy to GET requests:
workboxSW.router.setDefaultHandler({
handler: (args) => {
if (args.event.request.method === 'GET') {
return workboxSW.strategies.cacheFirst(args);
}
return fetch(args.event.request);
},
});
Alternatively, you could create a wildcard route that matches all requests, and take advantage of the fact that by default, routes will only match HTTP GET:
workboxSW.router.registerRoute(
/./, // This should match all requests.
workboxSW.strategies.cacheFirst({...}),
'GET' // This is the default, and can be left out.
);
Related
Let's say I have a very basic API with two sets of endpoints. One set queries and mutates properties about a User, which requires a username parameter, and one set queries and mutates properties about a Post, which requires a post ID. (Let's ignore authentication for simplicity.) I don't currently see a good way to implement this in a DRY way.
What makes the most sense to me is to have a separate Context for each set of routes, like this:
// post.ts
export async function createContext(
opts?: trpcExpress.CreateExpressContextOptions
) {
// pass through post id, throw if not present
}
type Context = trpc.inferAsyncReturnType<typeof createContext>;
const router = trpc
.router()
.query("get", {
resolve(req) {
// get post from database
return post;
},
});
// similar thing in user.ts
// server.ts
const trpcRouter = trpc
.router()
.merge("post.", postRouter)
.merge("user.", userRouter);
app.use(
"/trpc",
trpcExpress.createExpressMiddleware({
router: trpcRouter,
createContext,
})
);
This complains about context, and I can't find anything in the tRPC docs about passing a separate context to each router when merging. Middleware doesn't seem to solve the problem either - while I can fetch the post/user in a middleware and pass it on, I don't see any way to require a certain type of input in a middleware. I would have to throw { input: z.string() } or { input: z.number() } on every query/mutation, which of course isn't ideal.
The docs and examples seem pretty lacking for this (presumably common) use case, so what's the best way forward here?
This functionality has been added in (unreleased as of writing) v10. https://trpc.io/docs/v10/procedures#multiple-input-parsers
const roomProcedure = t.procedure.input(
z.object({
roomId: z.string(),
}),
);
const appRouter = t.router({
sendMessage: roomProcedure
.input(
z.object({
text: z.string(),
}),
)
.mutation(({ input }) => {
// input: { roomId: string; text: string }
}),
});
I want to use the chrome proxy API. I have this code in my background script but it will not work
const proxyData = []
const fetchProxyData = () => {
axios({
method: "GET",
baseURL: "https://api.getproxylist.com/proxy",
params: {
protocol: "http",
allowsPost: 1,
allowsHttps: 1,
anonimity: "high anonymity"
}
}).then( (response) => {
console.log(response.data)
proxyData.push(response.data)
})
}
fetchProxyData();
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
host: proxyData.ip,
port: proxyData.port
}
}
}
chrome.proxy.settings.set({
value: config,
scope: "regular"
}, () => {
console.log(`proxy configured with data: ${proxyData}`)
})
I get this error in background page console: background.js:55 Uncaught TypeError: Invalid invocation
I've tried with the example provided with the proxy api documentation and the error will not occur. Maybe it's caused by the config object? To set the proxy as you can see in the code, I'm using an ajax call, maybe is this the problem?
is there any fix?
I have also faced the same problem when I find the solution, it was silly mistake.
I had passed string value to port.
Please make sure you are passing integer value to port
Close. Couple things.
One, you need to fetch your data before calling Chrome's proxy API. And two, your getting the properties for your config from the proxyData array, not the JSON object from your axios call.
You need to do something like this:
const proxyData = [];
const fetchProxyData = () => {
axios({
method: "GET",
baseURL: "https://api.getproxylist.com/proxy",
params: {
protocol: "http",
allowsPost: 1,
allowsHttps: 1,
anonimity: "high anonymity"
}
}).then((response) => {
const {data} = response;
proxyData.push(data);
const config = {
mode: "fixed_servers",
rules: {
singleProxy: {
host: data.ip,
port: data.port
}
}
};
chrome.proxy.settings.set({
value: config,
scope: "regular"
}, () => {
console.log(`proxy configured with data: ${data}`)
});
})
};
fetchProxyData();
What's happening with your code...The properties host and port in singleProxy (in config) are being assigned undefined because those properties don't exist in the array proxyData. They exist in the object returned from your axios call (above, data).
The undefined keys caused Chrome to throw an Invalid Invocation error.
For anyone else getting the Invalid Invocation issue, it seems to me the problem usually lies within the config your passing into Chrome. In particular the types of each key.
In my case, I was passing in the port as a string when it needed to be a number.
i am pretty new to google workbox and try to add a custom plugin.
(Google workbox guide: Using Plugins)
I have registered routes - example:
/**
* Caching routes .de .com .test and .html
*/
workbox.routing.registerRoute(
new RegExp('\\.(?:html|de/|com/|test/)$'),
new workbox.strategies.StaleWhileRevalidate({
// Use a custom cache name.
cacheName: 'html-cache',
plugins: [
... <- here should be my plugin
new workbox.expiration.ExpirationPlugin({
// Cache for a maximum of 3 days.
maxAgeSeconds: 3 * 24 * 60 * 60,
}),
],
})
);
and the example plugin file from google:
const myPlugin = {
cacheWillUpdate: async ({request, response, event}) => {
// Return `response`, a different `Response` object, or `null`.
return response;
},
cacheDidUpdate: async ({cacheName, request, oldResponse, newResponse, event}) => {
// No return expected
// Note: `newResponse.bodyUsed` is `true` when this is called,
// meaning the body has already been read. If you need access to
// the body of the fresh response, use a technique like:
// const freshResponse = await caches.match(request, {cacheName});
},
cacheKeyWillBeUsed: async ({request, mode}) => {
// `request` is the `Request` object that would otherwise be used as the cache key.
// `mode` is either 'read' or 'write'.
// Return either a string, or a `Request` whose `url` property will be used as the cache key.
// Returning the original `request` will make this a no-op.
return request;
},
cachedResponseWillBeUsed: async ({cacheName, request, matchOptions, cachedResponse, event}) => {
// Return `cachedResponse`, a different `Response` object, or null.
return cachedResponse;
},
requestWillFetch: async ({request}) => {
// Return `request` or a different `Request` object.
return request;
},
fetchDidFail: async ({originalRequest, request, error, event}) => {
// No return expected.
// NOTE: `originalRequest` is the browser's request, `request` is the
// request after being passed through plugins with
// `requestWillFetch` callbacks, and `error` is the exception that caused
// the underlying `fetch()` to fail.
},
fetchDidSucceed: async ({request, response}) => {
// Return `response` to use the network response as-is,
// or alternatively create and return a new `Response` object.
return response;
}
};
My problem is now that i cant add this to the plugins section.
I tried several notations and nothing works.
What would i like to do?
I want to compare the old content from the cache with the newer response.
(I know there is a class who can do this -> BroadcastCacheUpdate).
But it also doesnt work for me. If anyone has some configuration examples (for version 5) i would appreciate it.
I hope you guys can help me.
Greetings
Simon
I'm fairly sure that you're going through the right steps, so I'm not sure what's not working. But basically, with just the relevant bits, it should look like:
const customPlugin = {
cacheDidUpdate: async (args) => {
console.debug('cacheDidUpdate called with', args);
// Do something with args.oldResponse and args.newResponse
},
};
workbox.routing.registerRoute(
new RegExp('\\.(?:html|de/|com/|test/)$'),
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'html-cache',
plugins: [
customPlugin,
],
})
);
Assuming that works for you, and you see the logged message, that should help put you on the right path.
I'm trying to override ajax timeout in a Documentum xCP application.
Ext.Ajax.setTimeout(120000) and Ext.override(Ext.data.proxy.Ajax, { timeout:120000 }) didn't help. Every time an instance of Ext.data.proxy.Ajax is created, it has timeout: 30000. Maybe it's reverted after my call, but I don't know how to check this.
With Ext.override I can create new properties in Ext.data.proxy.Ajax prototype, but existing properties don't change.
I debugging my app in Chrome and using the special parameter in app url to load the debug version of ext-all script.
upd:
If I call
Ext.override(Ext.data.proxy.Ajax, { timeout:120004 })`
just once, then
Ext.data.proxy.Ajax.prototype.getConfigurator().values["timeout"]==120004
Ext.data.proxy.Ajax.prototype.timeout==30000
The value 30000 is used in Ext.data.proxy.Ajax.doRequest().
If I call it again:
Ext.override(Ext.data.proxy.Ajax, { timeout:120005 })`
then
Ext.data.proxy.Ajax.prototype.getConfigurator().values["timeout"]==120004
Ext.data.proxy.Ajax.prototype.timeout==120005
I'm not familiar with Documentum xCP but on my web application using Ext.js (v.4.1), I used the following snippet to change general timeout:
Ext.onReady(function(){
Ext.Ajax.timeout = 150000;
});
Maybe the onReady event is the key.
If you want to override the proxy defaults, use:
Ext.define(null, {
override: 'Ext.data.proxy.Server',
config: {
timeout: 120000
}
});
If you want to override the proxy defaults. Put this class under app/overrides folder.
Ext.define('Ext.overrides.data.proxy.Proxy', {
override: 'Ext.data.proxy.Proxy',
timeout: 10000,
completeOperation: function(operation) {
try {
this.callParent(operation);
} catch (e) {
}
}
});
Or add your view model as give below
Ext.define('Model', {
extend: 'Ext.app.ViewModel',
alias: ..
stores: {
xstore: {
model: 'type'
proxy: {
type: 'ajax',
timeout: 90000, // increasing time.
url: url
}
}
}
});
For some odd reason, iron-router randomly returns undefined.
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
before: function () {
var id = this.params._id;
var poll = Polls.findOne({_id: id});
console.log(poll);
var ip_array = poll.already_voted;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
ip_voted = ip_array.indexOf(data.host);
if (ip_voted > -1) {
Router.go('pollyResults', {_id: id});
}
});
},
data: function() {
return Polls.findOne({_id: this.params._id});
}
});
Sometimes it is returning normally while other times it just returns undefined.
Is there any reason behind this?
The problem occurs because the Polly collection is sometimes populated and at other times unpopulated when the route executes.
This problem can be prevented by explicitly waiting on a subscription using waitOn option in the route configuration.
From the docs:
By default, a new Meteor app includes the autopublish and insecure packages, which together mimic the effect of each client having full read/write access to the server's database. These are useful prototyping tools, but typically not appropriate for production applications. When you're ready, just remove the packages.
To remove the packages, call meteor remove <package-name>.
Then you need to explicitly publish records which you want to see on the client on the server:
server/publications.js:
Meteor.publish('all_of_polly', function () { return Polls.find({}); });
And subscribe to it on the client:
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
waitOn: function () { return Meteor.subscribe('all_of_polly'); }
// ...
});