Google OneTap in Angular Universal - javascript

I'd like to implement Google OneTap in my Angular Universal app (that uses SSR). I'm using Angular 11, and the following script was working before converting the app to Angular Universal:
initGoogleOneTap() {
const domain = window.location.hostname;
google.accounts.id.initialize({
client_id: environment.GOOGLE_OAUTH_CLIENT_KEY,
cancel_on_tap_outside: false,
auto_select: true,
state_cookie_domain: domain,
callback: (response: any) => {
this.oneTapLogIn(response.credential)
.catch( (error) => {
console.error('Error logging in with Google One Tap: ', error);
});
},
});
google.accounts.id.prompt( (notification: any) => {
if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
// try next provider if OneTap is not displayed or skipped
}
});
}
I am using declare var google: any at the top of the script.
The error I am getting is: ERROR ReferenceError: google is not defined

First, load client library by putting this in your index.html
<script src="https://accounts.google.com/gsi/client" async defer></script>
Then, implement your component like this
declare var window: any;
ngOnInit() {
window.google.accounts.id.initialize({
client_id: 'YOUR CLIENT ID',
callback: this.handleGgOneTap.bind(this)
});
window.google.accounts.id.prompt();
}
handleGgOneTap(resp) {
console.log('handleGgOneTap ', resp);
}

Related

Keycloak Javascript failed to inicialize

I'm trying to use Keycloak with JavaScript and these are the steps that I followed.
I create a client inside KeyCloak admin panel.
Link to image
I copy the .json file to my apache folder.
{
"realm": "master",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "test",
"public-client": true,
"confidential-port": 0
}
I go to my index.html and I add these two lines for calling the script.
<script src="keycloak.js"></script>
<script>
function initKeycloak() {
const keycloak = new Keycloak();
keycloak.init().then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
}
</script>
this is what i have in myLogical.js
var keycloak = new Keycloak();
function initKeycloak() {
keycloak.init({onLoad: 'login-required'}).then(function() {
constructTableRows(keycloak.idTokenParsed);
pasteToken(keycloak.token);
}).catch(function() {
alert('failed to initialize');
});
}
function constructTableRows(keycloakToken) {
document.getElementById('row-username').innerHTML = keycloakToken.preferred_username;
document.getElementById('row-firstName').innerHTML = keycloakToken.given_name;
document.getElementById('row-lastName').innerHTML = keycloakToken.family_name;
document.getElementById('row-name').innerHTML = keycloakToken.name;
document.getElementById('row-email').innerHTML = keycloakToken.email;
}
function pasteToken(token){
document.getElementById('ta-token').value = token;
document.getElementById('ta-refreshToken').value = keycloak.refreshToken;
}
var refreshToken = function() {
keycloak.updateToken(-1)
I tried to download the file keycloak.js and put it directly on my root folder but it happen the same problem.
These is the message I got when I try to open the page
I'm confused about point 1, does keycloak automatically load configuration from json file in Apache folder? Let's assume that no, and I think that where your problem lies, you're not passing config param to keycloak constructor.
How to initialize keycloak:
const initKeycloak = async () => {
//you can hardcode these values for now just to see if everything works
const config = { url: 'http://localhost:8080/auth', realm: 'master', clientId: 'test'};
const keycloak = new Keycloak(config);
await keycloak
.init({ onLoad: 'login-required' })
.then(isAuthenticated => {
//user is authenticated
})
.catch(error => { console.log('keycloak error', error); });
}
Another important thing is that keycloak-js library version (in package.json) must match keycloak server version. Sometimes different versions work with each other but it's always best practice that keycloak-js version matches keycloak server version.
You can also look here: https://github.com/m-s7/react-core/blob/devel/src/services/keycloak-service.ts this is my repo with working keycloak-js implementation.

How do I include tags in the new #sentry/node API?

Before '#sentry/node' was released, I used the raven module. Including tags with all of my errors was as simple as including a tags property in the options object when configuring Raven.
Raven.config(DSN, {
tags: {...}
})
How do I include tags when using the new API? So far I've tried:
Sentry.init({
dsn: DSN,
tags: {
process_name: 'webserver',
},
})
and
Sentry.configureScope(scope => {
scope.setTag('process_name', 'webserver')
})
but neither attempt works.
According to docs
You can use Sentry.configureScope to configure scope, which including tag, of a Sentry instance.
Sentry.configureScope((scope) => {
scope.setTag("my-tag", "my value");
scope.setUser({
id: 42,
email: "john.doe#example.com"
});
});
Or you can use Sentry.withScope if you only want to send data with one specific event.
Sentry.withScope(scope => {
scope.setTag("my-tag", "my value");
scope.setLevel('warning');
// will be tagged with my-tag="my value"
Sentry.captureException(new Error('my error'));
});
// will not be tagged with my-tag
Sentry.captureException(new Error('my other error'));
You've to use Sentry.configureScope, but as mentioned in the sdk docs Note that these functions will not perform any action before you have called init().
This should work, otherwise you would have to contact sentry:
const Sentry = require('#sentry/node');
Sentry.init({
dsn: '__DSN__',
// ...
});
Sentry.configureScope(scope => {
scope.setExtra('battery', 0.7);
scope.setTag('user_mode', 'admin');
scope.setUser({ id: '4711' });
// scope.clear();
});

Load Angular 5 component after gapi has been loaded

I am writting an angular 5 app using the CLI.
I am using gapi to retrieve user data in order to populate a form.
The client script is included in the index.html file :
<head>
<meta charset="utf-8">
<script src="https://apis.google.com/js/client.js"></script>
...
</head>
Here is my component with the call :
userProfileModel: UserProfileModel = new UserProfileModel();
ngOnInit() {
this.form = this.toFormGroup();
this.onFormChanges();
this.userService
.getUserById(this.userId)
.then(usr => {
this.mapModel(usr.result['profile']);
})
.then(() => {
this.form.patchValue(this.userProfileModel);
});
}
And the userService's method :
declare var gapi: any;
export class UserService {
getUserById(id: number) {
return gapi.client.request({
method: 'GET',
'path': this.constants['endpoint_user_getbyid'] + '/' + id,
'root': this.constants['api_url']
});
}
...
}
Problem is, gapi seems to do not be initialized right after my component has finished loading : I have to set a >500ms timeout to be able to use it, and it's ugly.
This code gives me the following error :
ERROR TypeError: Cannot read property 'request' of undefined
Please not :
1- I haven't installed anything with npm / yarn, I am simply using the script with the gapi var declaration.
2 - after every build, the code works without any error and populates my form the first time the page is loaded, then after one refresh, it fails everytime.
How can I tell angular to load client.js at startup before all the components ?
Thanks !
Thanks to #ADarnal I finally found a solution by reading this topic :
How to pass parameters rendered from backend to angular2 bootstrap method
I followed the same exact process described in computeiro's answer.
My equivalent of the "BackendRequestClass" class is a GapiService.
In this Gapi service, the load method allows me to load gapi before any other call is executed :
/* GapiService */
loadGapi() {
return new Promise((resolve, reject) => {
gapi.load('client', () => {
gapi.client.init({apiKey: 'AIzaSyD5Gl9...'}).then(() => {
resolve(gapi.client);
})
});
});
}
// Method used in any component
getUserById(id: number) {
return gapi.client.request({
method: 'GET',
'path': this.constants['endpoint_user_getbyid'] + '/' + id,
'root': this.constants['api_url']
});
}
Finally; in my component, i inject this gapiService and I am able to use client.request on component init !
ngOnInit() {
this.gapiService
.getUserById(5066549580791808)
.then(
...
});

Integrating G Analytics to a Node.js API

I am currently stuck with this project : integrating GA to my REST API so I can retrieve metrics and dimensions.
I must highlight that I am running the API on localhost, it may explain why it does not work...?
I tried to integrate GA following different ways :
1. Using universal-analytics (https://github.com/peaksandpies/universal-analytics/blob/master/AcceptableParams.md)
const ua = require('universal-analytics');
require('dotenv').config({path: '../.env'});
// to retrieve the number of connexions to the homepage
server.route({
method: 'GET',
path: '/homepage',
handler: function (request, reply) {
...
const visitor = ua(process.env.UA_ID);
visitor.pageview('/bookmarks', 'http://localhost:4000', 'Homepage', (err) => {
if (err) {
throw err;
}
});
return reply.view('index');
}
});
// I have pretty much the same thing at the end of a POST endpoint handler
// to retrieve the number of new data posted
handler: function (request, reply) {
...
const user = ua(process.env.UA_ID, userId(request.auth.credentials.username));
user.event('Bookmarks', 'new_bkm_created', payload.title, (err) => {
if (err) {
throw err;
}
});
}
2. Creating a trackEvent function (https://cloud.google.com/appengine/docs/flexible/nodejs/integrating-with-analytics)
in google.js
const got = require('got');
module.exports = function trackEvent (propertyId, category, action, options) {
const data = {
v: '1', // API version
tid: propertyId, // Tracking / Property ID.
t: 'event', // Event hit type.
ec: category, // Event category.
ea: action // Event action.
};
if (options) {
if (options.label) {
data.el = options.label; // Event label.
}
if (options.value) {
data.ev = options.value; // Event value.
}
}
return got.post('http://www.google-analytics.com/collect', {
body: data,
form: true
});
};
in app.js
require('dotenv').config({path: '../.env'});
const trackEvent = require('./google');
const postHandler = (request, reply) => {
...
trackEvent(process.env.UA_ID, 'Thing', 'new_thing_created').then(() => {
return reply(bookmark).code(201);
}
}
3. Pasting the website tracking code from Admin --> Property ---> Tracking Info (https://developers.google.com/analytics/devguides/collection/analyticsjs/)
<script>
(function(i,s,o,g,r,a,m)i['GoogleAnalyticsObject']=ri[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com, 'analytics.js','ga');
ga('create', 'UA-XXX-whatev', 'auto');
ga('send', 'pageview');
</script>
I also tried the async version proposed on the link on the third title. The thing is, I do not like this way of doingm I do not understand how to launch events and page views from handlers with ga created in the front end...
Anyway, none of them works... I have no error, but nothing appears in G Analytics.
Just so you know, I used example.com in the default URL as it does not work with a localhost url.
Do you have any lead to help me with this?
Thank you so much in advance!
-- smgr
Turned out one of the three methods worked: the first one with universal-analytics. One needs to be patient to see the first tracked events. I warmly recommend this module though, very developer-friendly ;)

Ionic Cordova can not share video on social sites

I am trying to use the cordova social sharing plugin for sharing video on social sites. So far what I have achieved is, I have successfully captured video using following code -
var options = {
limit: 1,
duration: 15
};
$cordovaCapture.captureVideo(options).then(function (videoData) {
$scope.videoUrl = videoData[0].fullPath;
}, function (err) {
// An error occurred. Show a message to the user
//alert("video error : "+err);
});
I can successfully find the captured video files url but unfortunately I can not share them to the social media sites. I have tried both of the following methods -
$cordovaSocialSharing
.share(message, subject, file, link)
and
$cordovaSocialSharing
.shareViaTwitter(message, image, link)
Now my question is -
Is there any way to share video through this approach?
If not, please let me know if there is any possible way for this.
N.B. : I have already bothered the Google a lot.
Thanks in advance.
my problem was passing a bad filePath, so i found a solution like below :
import {CaptureError, MediaFile, MediaCapture, CaptureImageOptions, Transfer} from "ionic-native";`
declare let cordova: any;
private static options = {
message: '', // not supported on some apps (Facebook, Instagram)
subject: '', // for email
files: [''], // an array of filenames either locally or remotely
url: ''
};
videoOptions: CaptureImageOptions = {limit: 1};
videoData: any;
captureVideo() {
MediaCapture.captureVideo(this.videoOptions)
.then(
(data: MediaFile[]) => {
this.videoData = data[0];
const fileTransfer = new Transfer();
fileTransfer.download(this.videoData.fullPath, cordova.file.applicationStorageDirectory + 'fileDir/filename.mp4').then((entry) => {
this.options.message = " Your message";
this.options.subject = "Your Subject";
this.options.files = [entry.toURL()];
this.options.url = "https://www.google.com.tr/";
SocialSharing.shareWithOptions(this.options);
}, (error) => {
});
},
(err: CaptureError) => {
}
);
}
As you see above, i just copy my video file to applicationStorageDirectory

Categories