I have written a progressive web app, following all available guides and examples, but for some reason when I click the Add to homescreen button, I keep getting this mysterious error:
Site cannot be installed: does not work offline
The major difference between my PWA and the examples, is that mine is running purely in a non-root path of the domain, so I have had to add extra paths to the configs in various places so the app is restricted to the non-root folder.
The Google Lighthouse site doesn't help much either, giving a very similar message.
Can anyone suggest what this error might be caused by?
Update:
Looks like Google also picked up on the quick hack and the warning has returned.
So since of Chrome93 (AUG-2021) the quick hack, will not work anymore :
self.addEventListener('fetch', function(event) {})
Solution working "for now" (since we never know what requirements Google will add later on)
I've found a nice article which provides with a few solutions, the first one the author provides is Network-Falling-Back-To-Cache strategy:
your service worker will first try to retrieve the resource from your
server. Then when it can’t do that — because for example, you’re
offline — retrieve it from the cache (if it exists there).
self.addEventListener('fetch', function(event) {
event.respondWith(async function() {
try{
var res = await fetch(event.request);
var cache = await caches.open('cache');
cache.put(event.request.url, res.clone());
return res;
}
catch(error){
return caches.match(event.request);
}
}());
});
You can find all the information and alternative solutions in the article:
https://javascript.plainenglish.io/your-pwa-is-going-to-break-in-august-2021-34982f329f40
I hope this will help futur visitors.
Original answer:
Also you need to define fetch listener in a service worker file:
this.addEventListener('fetch', function (event) {
// it can be empty if you just want to get rid of that error
});
So it took me a couple of hours, but I eventually figured out that there is a required scope parameter that you need to specify in the client JavaScript when connecting to the serviceworker, if it's not running on the root (/) path.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js?v2', {
scope: '.' // <--- THIS BIT IS REQUIRED
}).then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
You can see the working product here:
App: https://stampy.me/pwgen/
Manifest file: https://stampy.me/pwgen/manifest.json
ServiceWorker file: https://stampy.me/pwgen/sw.js
App script: https://stampy.me/pwgen/script.js (scroll to bottom for PWA code)
I hope my pain can save someone else some time.
Related
I am learning firebase and I checked this tutorial regards the authentication. (https://www.youtube.com/watch?v=rbuSx1yEgV8&t=502s). In this video, the emulator seems to be essential, however I want to communicate with the server. How do I do it? If I do not initialize the auth emulator ( by removing the connectEmulator() function) I just get the error 'auth/network-request-failed'.
const firebaseConfig = {
//...
};
const user = {
email: 'user#test.me',
password: 'test1234'
}
function func() {
createUserWithEmailAndPassword(auth, user.email, user.password)
.then((userCredential) => {
const user = userCredential.user;
console.log(user)
})
.catch((error) => {
console.log(error)
// ..
});
}
As you can see from the minute 7:37 of that video, I am getting his issue! So I assume I am following the wrong approach. Can someone help me? I would be really grateful.
You should be able to authenticate with the server.
The emulator is optional. Personally I rarely use it, and essentially always use the real online Firebase server. However there are many steps before you are able to authenticate with the server.
Step 1. Check you have copied the configuration correctly
Go to this link, but replace PROJECT_ID with your actual project Id:
https://console.firebase.google.com/u/0/project/PROJECT_ID/settings/general/
Check that you have correctly copied the value of this into your app code from that page. If you have not "added an app", you may need to click "Add app", to get this config to display.
const firebaseConfig = {
... blah blah ...
};
Step 2. Check that you have enabled a "Sign-in provider"
Go to this link (again PROJECT_ID should be replaced by your project Id):
https://console.firebase.google.com/u/0/project/PROJECT_ID/authentication/providers
At least one of the providers needs to be switched on, like so:
Step 3. Your code looks good.
I assume you have set up auth correctly - we can't see that in the snippet above.
Please paste into your question the exact error message you are seeing on the console, as text.
You might want to intensify the debugging as follows:
function func() {
console.log(`user: ${JSON.stringify(user,null,2)}`)
createUserWithEmailAndPassword(auth, user.email, user.password)
.then((userCredential) => {
console.log(`userCredential.user: ${JSON.stringify(userCredential.user,null,2)}`)
})
.catch((error) => {
console.error(error)
});
}
A small thing, but I suggest avoiding using the same variable name, user, for two different things. Javascript will keep them separate, but we as programmers sometimes will get muddled when looking back at the code.
Step 4. Make sure you have authorised the domain you are using.
Go to:
https://console.firebase.google.com/u/0/project/PROJECT_ID/authentication/settings
Make sure you have authorised the domain from which you are "calling" the Firebase server.
If your app is running on "127.0.0.1" instead of "localhost", you might need to add that IP address too. Or if you have deployed, the deployed domain.
I am currently replacing the gapi.oauth2 package, by using the TokenClient according to the guide and everything works fine.
global.google.accounts.oauth2
.initTokenClient({
client_id: CONFIG.google.clientId,
scope: 'https://www.googleapis.com/auth/drive.readonly',
ux_mode: 'popup',
callback(tokenResponse) {
if (tokenResponse && !tokenResponse.error) {
onSuccess(tokenResponse.access_token);
return;
}
onError(tokenResponse.error || 'google authentication failed');
},
})
.requestAccessToken({});
The only issue is that we are not using the gapi.client and would prefer avoiding loading that package as we are only using the token to show a picker by using google.picker.PickerBuilder.
Now after the initialization the GSI package tries to use gapi.client.setToken() which obviously fails as the package is not loaded.
[GSI_LOGGER-TOKEN_CLIENT]: Set token failed. Gapi.client.setToken undefined.
So now i could not find anything in the reference on how to prevent that call to happen, nor how to at least suppress the warning by not e.g hacking in a noop as a placeholder.
Does anyone know if there is any official way to deal with that ?
In my tests the access token is successfully returned to the callback handler.
It looks to be an incorrectly generated warning, annoying yes, but functionally you're okay. A later, official release should remove the warning with no action on your part required.
The new API of GIS (Google Identity Services) maybe remove the setToken of the gapi.
I had to fix it by the code.
gapi = {client: {setToken: function(){ return; }}};
I am trying to add an app to our SharePoint Online site using the template from https://learn.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part and we get the error below when we deploy to SharePoint and add the app/Web part to a test SharePoint site. We are using TypeScript as the template uses.
Has anyone else encountered this issue or know where to look for the issue?
Found [object Object]Driver Display External Error: Error: AADSTS500011: The resource principal named https://driverdisplayexternal.azurewebsites.net was not found in the tenant named 7018324c-9efd-4880-809d-b2e6bb1606b6. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant. Trace ID: 358b22eb-cd2c-4091-b592-5a57cbc21d00 Correlation ID: ec96d656-1a36-42e2-a2b9-3ff78efc1e2e Timestamp: 2019-10-01 16:26:06Z
We have added a call to our own client as shown below. We are not sure why the resource principal was not found. The Tenant ID's match and things seem to be set up properly for authentication.
HelloWorldWebPart.ts
...
this.context.aadHttpClientFactory
.getClient('https://driverdisplayexternal.azurewebsites.net')
.then((client: AadHttpClient): void => {
client
.get('https://driverdisplayexternal.azurewebsites.net/api/values', AadHttpClient.configurations.v1)
.then((response: HttpClientResponse): Promise < Order[] > => {
this.domElement.innerHTML += 'Received a response from Driver Display External: ' + response;
return response.json();
})
.catch(error => {
this.domElement.innerHTML += 'Driver Display External Error: ' + error;
console.error(error);
});
});
...
package-solution.json
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "helloworld-webpart-client-side-solution",
"id": "**ID**",
"version": "4.1.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": false,
"webApiPermissionRequests": [
{
"resource": "DriverDisplayExternal",
"scope": "User.Read.All"
}
]
},
"paths": {
"zippedPackage": "solution/helloworld-webpart.sppkg"
}
}
Any help or direction to where the issue may be would be very appreciated. Thanks in advance!
Never used this API, but if I had to guess you need to change the value here:
.getClient('https://driverdisplayexternal.azurewebsites.net')
You can use either the client id / application id, or the application ID URI.
Sometimes this problem can occurr when you set a wrong name for the scope you are requesting access for or another configuration parameter.
I suggest to check carefully the scopes name, or maybe directly use the "copy" button from the Azure portal.
In my case it was a simple typo on a scope name.
Not sure if you figured the answer or not. When you used SPFx to request your own custom web api end point. there are couple steps:
request the permission so that you can go to SPO admin to approve the permission you request. for this case, the webApiPermissionRequests->resources needs to your AAD Application's Service Principal DisplayName. once you had AAD App create, you can run Get-AzureADServicePrincipal to get all your ServicePrincipal.
once you request the permission, from your code, you need to call AadHttpClient.getClient() to get aadHttpClient object based on the api resourceEndpoint you want, for this case, you need to pass your web api's Application ID URI which can be found from your AAD App's manifest->"identifierUris". General speaking, this should be something like api://[clientid] format. but you can change it to any unique value.
I hope it helps.
In my case i had to use the App Id when i was consuming a multi tenant API.
In my case, TenantId and ClientId were both ok.
They can be found in AAD. TenantId is right there on landing page:
and then on the same page click Applications then tab All Applications find your application there should be ClientId check if they match.
If that is still not enough, click on the application and find roles
For me, it was roles that were missing after adding those wheels started rolling again:
I haven't figured out how to get a response from restler in sails.js script. The goal is to import some data each day from a REST API to my database. So I chose to use sails.js script to be able to crontab the call.
When I search what I could use to contact the API. I saw a topic about restler which seem pretty easy to use.
Sadly when I try it I cannot catch the response from the API, I use this basic example
module.exports = {
friendlyName: 'test',
description: 'this is a test',
fn: async function () {
sails.log('Running custom shell script... (`sails run test`)');
var rest = require('restler');
rest.get('http://google.com').on('complete', function(result) {
sails.log("In callback")
if (result instanceof Error) {
sails.log('Error:', result.message);
this.retry(5000); // try again after 5 sec
} else {
sails.log(result);
}
});
sails.log('finished');
}
};
When I run the script this is what I get
info: Initializing hook... (`api/hooks/custom`)
info: Initializing `apianalytics` hook... (requests to monitored routes will be logged!)
info: ·• Auto-migrating... (alter)
info: Hold tight, this could take a moment.
info: ✓ Auto-migration complete.
debug: Running custom shell script... (`sails run test`)
debug: finished
I try other method and other URL too, but apparently I never get in the callback.
So I assume that the script doesn't "wait" the .on('complete') before continuing his execution.
I know, and use, on the database call .then to avoiding that, I think this is called a 'promise'. So from what I understand the problem is around that but sadly after searching, I don't find any answer to solve my issue.
I am not married to restler, and if another and better solution exists don't hesitate, the goal is again to get the content from a REST API from sails.js script.
Thank you for the time you take to answer my question.
While I am in a process of integrating Spotify API into Google Assistant app, implementing Account Linking,
getaddrinfo EAI_AGAIN api.spotify.com:443
This above error has been kept coming out in the console, although it seems to be working like nothing is wrong around API implementation. The access token is properly created and received and client and secret ids were filled without any typo. Also, I tested API calls on Spotify Console (https://developer.spotify.com/console/get-artist-albums/). No error was found. It fetched expected data from the Spotify server, so it should not be related to Account Linking and Spotify Server. The created code myself is below: I assume there is something wrong around spotify-web-api-node, node, npm, or firebase-functions?? I recently have done node versioning so I might did something wrong.
Node version: v7.9.0
spotify-web-api-node: ^4.0.0
firebase-functions: ^2.0.3
npm version: 6.4.1
Added engines: { "node": "8" } // this is in package.json to use asyn and await
app.intent(SomeIntent, async (conv, params) => {
console.log('user info', JSON.stringify(conv.user));
conv.ask('lets play'); //okay
const access_token = conv.user.access.token || ''; // okay
console.log('Your TOKEN information here: ' + access_token); // okay
spotifyApi.setAccessToken(access_token); // should be set correctly
let data = await findMusic(); // error in the findMusic func
conv.ask('found this song。', data.name); // so no data.name
});
function findMusic() {
return spotifyApi.getArtistAlbums('43ZHCT0cAZBISjO8DG9PnE').then((data) => {
console.log('artist song', data.body);
return data.body; //this does not return because error is produced
}).catch(err => {
console.log('Something went wrong!', err);
return err; // this error is WebapiError: getaddrinfo EAI_AGAIN api.spotify.com:443
});
}
UPDATE
#Nick-Felker mentioned in the comment below that external calls are allowed only through paid plans. So this might be the solution (not proved to be working right now because I am not using a paid plan. The detailed explanation below is quoted from An answer comment from another StackOverflow post
The Spark plan allows outbound network requests only to Google-owned services. Inbound invocation requests are allowed within the quota. On the Blaze plan, Cloud Functions provides a perpetual free tier. The first 2,000,000 invocations, 400,000 GB-sec, 200,000 CPU-sec, and 5 GB of Internet egress traffic is provided for free each month. You are only charged on usage past this free allotment. Pricing is based on total number of invocations, and compute time. Compute time is variable based on the amount of memory and CPU provisioned for a function. Usage limits are also enforced through daily and 100s quotas. For more information, see Cloud Functions Pricing.
UPDATE
In my case, the above solution worked. Hope this article helps others!
I got this error due to network problem. And solved when connected