Workbox - NetworkOnly executes even if I return false - progressive-web-apps

Using Workbox, I am trying to deal with graphql.
Anyone know why I am getting this error message from NetworkOnly? I am returning false in the route matcher, so I don't know why new NetworkOnly() is even executing. This only happens in offline mode (no network available).
The strategy could not generate a response for ... The underlying error is TypeError: Failed to fetch.
at NetworkOnly._handle (webpack://service-workers/./node_modules/workbox-strategies/NetworkOnly.js?:101:19)
at async NetworkOnly._getResponse (webpack://service-workers/./node_modules/workbox-strategies/Strategy.js?:155:24)
const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
maxRetentionTime: 24 * 60
});
const isGraphqlSubmitForm = async(event) => {
const clonedRequest = event.request.clone();
const body = await clonedRequest.json();
if (body?.operationName === 'submitForm') {
return true;
}
return false; // I MADE SURE THIS IS BEING RETURNED
};
registerRoute(
isGraphqlSubmitForm,
new NetworkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
);

Sorry about that—you're seeing that behavior because the truthiness of the return value from this function is used to determine whether a route matches, and an async function always returns a Promise, which is a "truthy" value.
There's a log message that warns about this if you're in Workbox's development mode.
If it's possible for you to add in a header on the requests you want to match, that would probably be the easiest way to synchronously trigger your route, since any attempt to access a request body needs to be async. Failing that, the cleanest approach would be to just match all of your GraphQL requests with a given route, and then inside of that route's handler, use whatever async logic you need to trigger different strategies for different types of traffic.

Related

Tell wether or not a mint call to a contract succeeded

I'm working on an NFT site in NextJS and trying to implement a redirect for the user after they successfully mint a token. Here's my mint code:
const mintToken = () => {
safeMint?.();
router.push('/success');
};
As you can see, after safeMint is called, I try to redirect to /success which is what happens. However, it redirects regardless of a successful mint, I want it to only redirect after the call to the smart contract succeeds. I've tried using callbacks and timeouts but nothing seems to work the way I've laid out above. Is there some way of getting or waiting for a success response before redirecting that I'm missing? Thanks!
Function return value is not available outside of EVM if you execute the function with a transaction.
You can wait for the transaction receipt. It contains the transaction status (success / revert), as well as event logs. Tx receipt is available only after the tx is included in a block.
Depending on your safeMint() implementation, it might mint tokens each time the transaction succeeds. But if your implementation allows for the function to succeed even without minting tokens, you might need to check the event logs to make sure that the NFT was really minted.
// transaction reverted
function safeMint() external {
require(failedCondition);
_mint(msg.sender, tokenId);
}
// transaction succeeded but no token was minted
function safeMint() external {
if (failedCondition) {
_mint(msg.sender, tokenId);
}
}
How to wait for the receipt with ethers:
const tx = await myContract.safeMint();
const txReceipt = await transaction.wait();
if (txReceipt.status) {
// not reverted
}
Docs:
https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse
https://docs.ethers.io/v5/api/providers/types/#providers-TransactionReceipt
in safeMint function inside contract, you can return the tokenId (or you could return true)
const mintToken =async () => {
const result=await safeMint?();
if(result){
router.push('/success');
}
};

Flutter Unit test http mock returns 400 when it should return 200

Not really sure what's going on, and every tutorial I find seems to set things up the same way.
Essentially I've mocked out an http.client using Mockito and I'm trying to stub an http get request like so:
test("Return 'has_on_boarded' when firebaseUid is valid", () async {
final client = MockClient();
const uid = "123";
when(client.get(Uri.parse("${dotenv.env['BASE_API_URL']}/users/$uid")))
.thenAnswer((_) async =>
http.Response('{"user":{"has_onboarded":true}}', 200));
expect(await UserRepository().initialize(client, uid), "has_on_boarded");
});
For some reason, I only get 400s and not the 200 or body I'm telling it to return with.
Update:
Here is the Flutter tutorial I'm using.
I did discover that using testwidgetsflutterbinding makes all http requests return 400, though since it is being mocked that seems weird.
I've removed that part, however, while it's no longer erroring http.Response('{"user":{"has_onboarded":true}} still resulting in {"user":{"has_onboarded":true}} being returned...

PostgreSQL: Read returning more quickly than write

I have two queries written with SequelizeJS
Read:
Object.getObject = async function(){
return await this.findOne();
}
Write:
Object.updateObject = async function(info){
const t = await sequelize.transaction();
const currentObject = await Object.findOne();
const updatedObject = currentObject.updateAttributes(info, t);
t.commit();
return updatedObject;
}
These queries are fired when you go to a URL in an API.
I trigger the write query first, and then in a promise that handles the successful write request, I get the updated object.
API.updateObject().then((res) => {
API.getObject();
})
Despite the GET HTTP request and read query firing after the UPDATE HTTP request returns and is handled as a promise. The read query completes first and returns an object model in a state prior to be being modified by the mentioned write query.
As in, the write query, despite being fired off first and indicating it was complete. Fires off the read query which completes and retrieves an older object modal before the write is complete.
Is there something I don't know with about PostgreSQL and how it handles the sequences of queries when reading and writing to the same object?

Bluebird Promise each in mocha/chai test not working

I would like some help to determine why my unit test in a sails.js app is not working as expected.
I am using mocha, chai and bluebird promise library on a sails.js app.
What I want to achieve:
Create a test for TagsService.create(name) method, which accepts a name
parameter.
Test that this method will not create a new tag record based on invalid names I pass
The name parameter is required and should be less than 121 characters long
What I currently have:
// Test the 'create' method
describe('Method \'create\' test result: \n', function () {
// Test that name is required and less than 121 chars long
it('Must receive the name parameter and be less than 121 chars long', function(done) {
// It should not accept any of the following names
var names = ['',' ','thisstringislongerthanthemaxof121characterslongthisstringislongerthanthemaxof121characterslongthisstringislongerthanthema',[],[{}],[{test: 'test'}],'wrongchars*[]$£%fsf','$%#~}[','£$%jkdfi',' $%"£asdwdFDE','hD8U £$&{DS ds'];
sails.bluebird.each(names,function(name){
TagsService.create(name).then(function(data){
assert.propertyVal(data,'status','err','An error was NOT returned - even though names provided should be invalid');
});
}).then(function(){
done();
});
});
});
What happens is it seems to pass, even if I pass in a valid name or return null from the method.
Well, looks like I managed to solve it, after much trial and error.
Turns out I need to catch the done() callback from the Promise after the each method executed. Also needed to return the result of the tests done from the TagsService promise object. (Still not 100% sure this is the correct way to think about it..). Anyway the test seems to function properly now.
Here is my result:
var names = ['',' ','thisstringislongerthanthemaxof121characterslongthisstringislongerthanthemaxof121characterslongthisstringislongerthanthema',[],[{}],[{test: 'test'}],'wrongchars*[]$%fsf','$%#~}[','�$%jkdfi',' $%"�asdwdFDE','hD8U �$&{DS ds'];
sails.bluebird.each(names, function(name){
return TagsService.create(name).then(function(data) {
assert.property(data, 'status', 'create method did not return a status property');
assert(data.status === 'err', 'even with an invalid name parameter passed - it did not return an err status, which it must do with an invalid name.');
});
}).then(function(){
done();
}).catch(done);

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.