Error 'Login Required' while calling Google Analytics API from Google Spreadsheet - google-analytics

I use google scripts to fill Google Spreadsheet by Google Analytics data.
Problem: I get error: Login Required when I call =getCountOfNewUsers(B1,B2) from my Spreadsheet file. NOTE: when I run runTest() from google script project everything working OK.
FYI: I have followed this tutorial.
function runTest() {
try {
var results = getCountOfNewUsers(new Date(), new Date());
Logger.log(results.totalsForAllResults["ga:newVisits"]);
} catch(error) {
Logger.log(error.message);
}
}
function getCountOfNewUsers(date_from, date_till) {
date_from = Utilities.formatDate(date_from, 'GMT', 'yyyy-MM-dd');
date_till = Utilities.formatDate(date_till, 'GMT', 'yyyy-MM-dd');
var tableId = 'ga:*****';
// Make a request to the API.
var results = Analytics.Data.Ga.get(tableId, date_from, date_till, 'ga:newVisits', {});
if (results.getRows()) {
return results;
} else {
throw new Error('No views (profiles) found');
}
}

This is not solution to your question but it directs you to easy better solution to your problem if your interested!, you can easily setup this solution in an hour!
Details:
Better Than A writing our own script with Diligent Study and make Google scripts to fill Google Spreadsheet by Google Analytics data I use this Google Analytics Report Automation (Magic Script)
This Magic Script allow you to simplifies this process and makes it
easy to get the data you want so that you can focus on analysis and
reporting.
Custom API Dashboards - No Code Required it will save your burden for
Writing Your Own Script.
About the Error Try following things
Go to script editor, click on Resources > click on Use Google APIs.
Click on "Google APIs Console".
select APIs & auth > click on Registered apps.
Do Register your script project as web application.
select Certificate > Generate New Key .

This is an expected behavior. Custom Functions cannot ask users to give access to personal data. So they are not suitable for all types of services, specifically those which need authorization for personal data.
Check this url to know which services you can use - https://developers.google.com/apps-script/guides/sheets/functions#using_apps_script_services
Unlike most other types of Apps Scripts, custom functions never ask users to authorize access to personal data. Consequently, they can only call services that do not have access to personal data

Related

Custom logging from firebase function

I'm trying to follow this guide to put some custom logging into a firebase function. The function itself is running, and I can see the data being passed in (it's an https 'callable' function). But as soon as it hits the line where it tries to actually write that log entry, I get "Error: 7 PERMISSION_DENIED"
Since the console.log() calls write to the cloud logs, I'd assumed the firebase function has access to Cloud Logging. But perhaps it needs additional permission? I can't find any reference to where this should be set on that page though.
// Logging, logName, region, functions are provided by the surrounding app
const logging = new Logging()
const log = logging.log(logName)
const METADATA = {
resource: {
type: 'cloud_function',
labels: {
function_name: 'CustomLog',
region
}
}
};
exports = module.exports = functions.https.onCall(async data => {
const exVersion = 6
const exId = data.exId
console.log('***** exVersion:', exVersion, 'exId:', exId) // exId from caller
const entry = log.entry(METADATA, data.error) // data.error from caller
console.log('METADATA:', METADATA) // Shows in Logs Explorer
console.log('entry:', entry) // Shows in Logs Explorer
log.write(entry) // Results in Error: 7 PERMISSION_DENIED
return {
exVersion,
exId,
}
})
If I run it from the CLI using firebase function:shell, the log entry is created correctly, so I'm pretty confident the code is correct.
OK, I finally tracked it down. According to this answer, the service account used by firebase functions is {project-id}#appspot.gserviceaccount.com, and in my project, that account did not have the 'Logs Writer' role. Adding that role solves the problem.
I find it odd that the firebase functions don't need that role to log messages using console.log(), but perhaps that call is intercepted by the functions environment, and the logs are written as a different service account. It also explains why the functions running locally were able to write the logs, as they run using the 'owner' service account, which has full access.
According to the Firebase documentation page you have linked:
The recommended solution for logging from a function is to use the
logger SDK. You can instead use standard JavaScript logging calls such
as console.log and console.error, but you first need to require a
special module to patch the standard methods to work correctly:
require("firebase-functions/lib/logger/compat");
Once you have required the logger compatibility module, you can use console.log() methods as normal in your code.
Thus you might to require this library, however I am not sure this is producing your "Error: 7 PERMISSION_DENIED error, but you might also try some solutions that have worked for some members of the community.
Perhaps the logging API is not enabled in your project. You'll get a permission denied error when attempting to use it in that case.
It's a couple levels in, but the guide you linked points to
https://github.com/googleapis/nodejs-logging#before-you-begin, which includes a step to "Enable the Cloud Logging API"

Firebase and LinkedIn Auth integration unknown route

I've been examining this code base as an example of how to implement LinkedIn authorization to my project with a Firebase Backend. One thing I'm confused about is these lines:
var code = getURLParameter("code");
var state = getURLParameter("state");
var error = getURLParameter("error");
if (error) {
document.body.innerText = "Error back from the LinkedIn auth page: " + error;
} else if (!code) {
// Start the auth flow.
window.location.href = "/redirect";
}
at window.location.href = '/redirect', I believe it is meant to invoke the cloud function called "redirect". In my code base, it simply goes to an unknown route and triggers my fallback. Am I wrong about the purpose of this line of code? Does anyone know any possible reasons it's not triggering the cloud function (console says 0 invocations)? What other information should I look into to try to debug this?
To provide a bit fuller of an answer:
The example you provided relies on a Firebase.json file. This file provides configuration if (and only if) your application is hosted with Firebase hosting (see docs).
If you expect to host your app elsewhere, you'll need to make sure your /redirect path points to the Firebase function URL itself (probably something like https://us-central1-project-name.cloudfunctions.net/redirect). In the authorization flow, the LinkedIn module in the example repo then will redirect to either a default or a configured callback url.

How to access google drive from our meteor application?

I am building a meteor application.In many instances i need to access google service such as sharing,uploading files etc.
Gone through Google's guide, but some area is not clear.
Created a new project,then in overview enabled drive API.
Created Credentials-->Service Account Key--->App Engine.
Downloaded the private key.
And in my application there is a button when clicked it should access the google services such as if the button is intended for file sharing then it should access google's share service or if it is intended for file uploading then it should access google's upload to drive service and similarly I want to view the files uploaded to google drive in my application.
Somebody help me with the code since I am very new to meteor and this I am finding difficulty in developing
You can follow the quickstart Google provided. Use it as a stepping stone to familiarize yourself on the behavior, errors, best practices in using the api.
Here is the sample code for listing the file names and IDs:
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listFiles(auth) {
var service = google.drive('v3');
service.files.list({
auth: auth,
pageSize: 10,
fields: "nextPageToken, files(id, name)"
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var files = response.files;
if (files.length == 0) {
console.log('No files found.');
} else {
console.log('Files:');
for (var i = 0; i < files.length; i++) {
var file = files[i];
console.log('%s (%s)', file.name, file.id);
}
}
});
}
NOTE: Follow the prerequisite.
Enable the Drive Platform
Authorizing Your App with Google Drive
Further Readings that will help you with google integration
Google Developers Console help documentation
Google APIs Client for Node.js documentation
Drive REST API reference documentation
Tutorials
Here is a tutorial for implementation of Google OAuth 2.0 for server-to-server interactions for Meteor. You can also using JavaScript application that makes requests to the Drive API.

Accessing YouTube API in Meteor project for logged in user

I'm working on a new Meteor project which involves users logging into the site using their Google accounts through OAuth (I'm using the Meteor accounts-google package for this) and when signing in I need them to be able to see some data from the YouTube Analytics API for their YouTube channel. As of now the data I am trying to get is their total daily views, which I then hope to display on a chart for a specified time period.
I have added the following scopes to my accounts-google login system:
Meteor.loginWithGoogle({
requestPermissions: ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/yt-analytics.readonly', 'https://www.googleapis.com/auth/youtube.readonly'],
requestOfflineToken: true,
forceApprovalPrompt: true,
loginStyle: "popup"
});
This all seems to be working very well, once a user signs into the site and grants the site access to these I can then see the necessary info in my MongoDB database. Under user.services.google I can now see it has accessToken, idToken, expiresAt, refreshToken, etc.
I've also decided to use the google api nodejs client by implementing it through the meteorhacks:npm package for Meteor. I am using this to refresh tokens (as seen in this SO answer I found helpful).
Using the "Try It" API Explorer on the YouTube Analytics API Documentation page, I can get the type of data I'm looking for through this request:
GET https://www.googleapis.com/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2015-10-01&end-date=2015-10-31&metrics=views&dimensions=day&sort=-day&key={YOUR_API_KEY}
Now is where I've been completely stuck and really unsure of where to go from here. How can I implement this into my site? I've tried for quite some time now to make this work but everything I attempt isn't working, and there's no real direction. If anyone is willing to help out I'd greatly appreciate it. I'm fairly new to Meteor/JS/APIs so any information/examples is extremely appreciated, especially noob friendly stuff! ;)
One way to do it is to use a method and the http package: https://atmospherejs.com/meteor/http
Looking at the doc you provide, you may try something like this:
Define your method on the server side
// server-side
Meteor.methods({
getYoutubeReports: function(channelId, accessToken, params) {
params.ids = "channel=="+ channelId;
params.key = accessToken;
return HTTP.get("https://www.googleapis.com/youtube/analytics/v1/reports", {
params: params
});
}
});
You can then call it on the client side with the data you get from your the authentication (ie. CHANNEL_ID_OF_MY_USER & ACCESS_TOKEN_OF_MY_USER)
// client-side
var reports,
myParams = {
"start-date": "2015-10-01",
"end-date": "2015-10-31",
"metrics": "views",
"dimensions": "day",
"sort": "-day"
};
Meteor.call('getYoutubeReports', CHANNEL_ID_OF_MY_USER, ACCESS_TOKEN_OF_MY_USER, params, function(error, result) {
// store or do stuff with the result of the HTTP request here
console.log(result);
});
Feel free to custom myParams as your user need!
And if you want to some more tips about how to use HTTP request (really useful to call external API), The Meteor Chef wrote a really good article about it : https://themeteorchef.com/snippets/using-the-http-package/
I hope it helps!
I ended up using the percolate:google-api package to handle my API call.

Unauthorized client error with Google API using a service account to impersonate a connected email

I have been using the Google api client to access my companies' analytics data for the last 3 months using the method below, and it worked fine.
I then had to repeat the process with a different analytics account and I cannot get anything other than { "error" : "unauthorized_client", "error_description" : "Unauthorized client or scope in request." }
The account is set up on console.developers.google.com under the account I want to impersonate, and the details/.p12 key are copied correctly.
I am not on a google apps domain, so no delegation is needed. I cannot understand why one service account/impersonation works fine, but I cannot recreate for any other analytics account.
Here is what I am using:
function __construct ()
{
parent::__construct();
// accessed via google developer console
$client_email = '****.gserviceaccount.com';
$private_key = file_get_contents(__DIR__ .'/cm-suite.p12');
$scopes = array(\Google_Service_Analytics::ANALYTICS_READONLY);
$user_to_impersonate = '****EMAIL***';
$credentials = new \Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key,
'notasecret',
'http://oauth.net/grant_type/jwt/1.0/bearer',
$user_to_impersonate
);
$client = new \Google_Client();
$client->setAssertionCredentials($credentials);
if ($client->getAuth()->isAccessTokenExpired())
$client->getAuth()->refreshTokenWithAssertion();
// set reference for instance methods
$this->client = &$client;
}
The accounts are added correctly in Analytics, and all the secure details are correct (if I am not trying to use a sub account, the service account connects correctly).
This problem is programming language agnostic I believe as my example worked before using a different account - so must be a configuration issue, or something I have missed settings-wise.
I cannot understand why it works for one google account, and not another.

Resources