I can successfully request data from the google analytics core reporting api but am struggling to pull from the multi-channel funnels api. I am pretty sure my problem is with permission/access but I've gone through the documentation and can't figure out exactly what the problem is. I am using node and the googleapis library. Here is my code for pulling the google analytics information (This works)
var {google} = require('googleapis');
var key = require('./auth.json');
var viewID = 'XXXXXXX';
var analytics = google.analyticsreporting('v4');
var jwtClient = new google.auth.JWT(key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/analytics.readonly'],
null);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log('Yup, we got authorized!');
console.log(tokens)
}
});
var req = {
reportRequests: [{
viewId: viewID,
dateRanges: [{
startDate: '2020-11-01',
endDate: '2020-11-01'}],
metrics: [{expression: 'ga:pageViews'}],
dimensions: [{name: 'ga:city'}],
pageSize: 10
}],
};
analytics.reports.batchGet({
auth: jwtClient,
resource: req
},
function (err, response) {
if (err) {
console.log('Failed to get Report');
console.log(err);
return;
}
console.log('Success');
console.log(response.data)
}
);
This produces the desired output just fine. I tried to change the metrics and dimensions so I can pull some multi-channel funnel information that I want but am getting an error
var {google} = require('googleapis');
var key = require('./auth.json');
var viewID = 'XXXXXXX';
var analytics = google.analyticsreporting('v4');
var jwtClient = new google.auth.JWT(key.client_email,
null,
key.private_key,
['https://www.googleapis.com/auth/analytics.readonly'],
null);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log('Yup, we got authorized!');
console.log(tokens)
}
});
var req = {
reportRequests: [{
viewId: viewID,
dateRanges: [{
startDate: '2020-11-01',
endDate: '2020-11-01'}],
metrics: [{expression: 'mcf:sourceMedium'}],
dimensions: [{name: 'mcf:lastInteractionConversions'}],
pageSize: 10
}],
};
analytics.reports.batchGet({
auth: jwtClient,
resource: req
},
function (err, response) {
if (err) {
console.log('Failed to get Report');
console.log(err);
return;
}
console.log('Success - got something back from the Googlez');
console.log(response.data)
}
);
It is producing this
code: 400,
errors: [
{
message: 'Unknown dimensions(s): mcf:lastInteractionConversions, unknown metric(s): mcf:sourceMedium\n' +
'For details see https://developers.google.com/analytics/devguides/reporting/core/dimsmets.',
domain: 'global',
reason: 'badRequest'
}
]
I am pretty sure my problem is either I am requesting from the wrong source or my permissions are only available for ga and not mcf, but I don't fully understand the reason for the error or how to fix it, and the documentation hasn't provided much help. Any help would be appreciated, thanks.
You are using Reporting API V4 instead of Multi-Channel Funnels API V3: https://developers.google.com/analytics/devguides/reporting/mcf/v3/reference
The API you need provides a single method to request data:
analytics.data.mcf.get()
Related
I am creating a REST API service using Azure Function in nodeJs. The function is reading some data from Azure SQL and I want it to be returned. I am using tedious package to connect to Azure SQL Database.
const { Connection, Request } = require("tedious");
var data = [];
console.log("0." + data);
const config = {
authentication: {
options: {
userName: "------", // update me
password: "--------" // update me
},
type: "default"
},
server: "----.database.windows.net", // update me
options: {
database: "---", //update me
encrypt: true
}
};
module.exports = async function (context, req, resp) {
const connection = new Connection(config);
context.bindings.response = { status: 201, body: {"time": new Date().getUTCMinutes(), "data": data} };
connection.on("connect", err => {
if (err) {
console.error(err.message);
} else {
queryDatabase(context);
}
});
connection.connect();
//context.bindings.response = { status: 201, body: JSON.stringify(data) };
function queryDatabase(context) {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
`SELECT FirstName, LastName FROM Persons`,
(err, rowCount, data) => {
if (err ) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
}
);
request.on("row", columns => {
var row = {};
columns.forEach(column => {
row[column.metadata.colName] = column.value;
console.log("%s\t%s", column.metadata.colName, column.value);
data.push(row);
});
});
connection.execSql(request);
}
}
I can read data from Azure SQL Database and the console.log is printing data in the console.
console.log("%s\t%s", column.metadata.colName, column.value);
But while I am trying to bind the data to response, it always shows blank.
{
"time": 52,
"data": []
}
How and where to bind the context.bindings.response?
If I’ve understood you correctly, try this approach ...
// Construct response
const responseJSON = {
"name": "Some name",
"sport": "Some sport",
"message": "Some message",
"success": true
}
context.res = {
// status: 200, /* Defaults to 200 */
body: responseJSON,
contentType: 'application/json'
};
My goal is to allow people to add events to a calendar through my website (which uses Firebase currently.) I'm attempting to allow them to add the events to a service account; however, I'm having issues getting the service account authenticated. When I attempt to deploy my code, I get an 'unexpected token google' error at the await google.auth.getClient part. I have google API tools installed globally.
//ADD CALENDAR EVENT
const {google} = require('googleapis');
const calendar = google.calendar('v3');
//Authenticate Service Acounnt
function getCredentials() {
const filePath = path.join(__dirname, 'credentials.json';
if (fs.existsSync(filePath)) {
return require(filePath)
}
if (process.env.CREDENTIALS) {
return JSON.parse(process.env.CREDENTIALS)
}
throw new Error('Unable to load credentials')
}
//Create Calendar Event
function addEvent(event) {
return new Promise(function(resolve, reject) {
calendar.events.insert({
calendarId: 'primary',
resource: {
'summary': event.eventName,
'description': event.description,
'start': {
'dateTime': event.startTime,
'timeZone': TIME_ZONE,
},
'end': {
'dateTime': event.endTime,
'timeZone': TIME_ZONE,
},
},
}, (err, res) => {
if (err) {
console.log('Rejecting because of error');
reject(err);
}
console.log('Request successful');
resolve(res.data);
});
});
}
//Add Event To Service Acount
exports.addEventToCalendar = functions.https.onRequest((request, response) => {
const eventData = {
eventName: request.body.eventName,
description: request.body.description,
startTime: request.body.startTime,
endTime: request.body.endTime
};
const credentials = getCredentials();
const client = await google.auth.getClient({
credentials,
scopes: 'https://www.googleapis.com/auth/calendar',
})
addEvent(eventData, client).then(data => {
response.status(200).send(data);
return;
}).catch(err => {
console.error('Error adding event: ' + err.message);
response.status(500).send(ERROR_RESPONSE);
return;
});
});
I've been following a combination of these two tutorials:
https://medium.com/zero-equals-false/integrating-firebase-cloud-functions-with-google-calendar-api-9a5ac042e869
https://dev.to/mornir/create-a-service-account-to-authenticate-with-google-5b1k
I've spent a lot of time googling what could be wrong, but to be honest, this google authentication stuff confuses the heck out of me. I'd appreciate whatever help I can get, thanks :)
You want to create new event to the Google Calendar using the service account.
credentials.json is the credential file of the service account.
You want to achieve this using googleapis with Node.js.
You have already been able to use Calendar API.
If my understanding is correct, how about this modification? Please think of this as just one of several possible answers.
Modification points:
When the service account is used, you can use google.auth.JWT instead of google.auth.getClient.
In your script, client is given to addEvent(eventData, client). But at function addEvent(event) {}, client is not used.
About path.join(__dirname, 'credentials.json';, ) is required to be added. And I think that const path = require("path"); is also required to added.
Modified script:
//ADD CALENDAR EVENT
const { google } = require("googleapis");
const path = require("path"); // Added
// --- I added below script.
const key = require(path.join(__dirname, 'credentials.json'));
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
["https://www.googleapis.com/auth/calendar"],
null
);
const calendar = google.calendar({ version: "v3", auth: jwtClient });
// ---
//Create Calendar Event
function addEvent(event) {
return new Promise(function(resolve, reject) {
calendar.events.insert(
{
calendarId: "primary",
resource: {
summary: event.eventName,
description: event.description,
start: {
dateTime: event.startTime,
timeZone: TIME_ZONE
},
end: {
dateTime: event.endTime,
timeZone: TIME_ZONE
}
}
},
(err, res) => {
if (err) {
console.log("Rejecting because of error");
reject(err);
return;
}
console.log("Request successful");
resolve(res.data);
}
);
});
}
//Add Event To Service Acount
exports.addEventToCalendar = functions.https.onRequest((request, response) => {
const eventData = {
eventName: request.body.eventName,
description: request.body.description,
startTime: request.body.startTime,
endTime: request.body.endTime
};
addEvent(eventData) // Modified
.then(data => {
response.status(200).send(data);
return;
})
.catch(err => {
console.error("Error adding event: " + err.message);
response.status(500).send(ERROR_RESPONSE);
return;
});
});
Note:
This modified script supposes that the object of request.body can be used for resource of calendar.events.insert().
Is TIME_ZONE declared elsewhere? Please be careful this.
When the email of service account is not shared with the Google Spreadsheet, the event cannot be created. So please be careful this.
References:
google-api-nodejs-client
Events: insert
If I misunderstood your situation and this was not the direction you want, I apologize.
I have imported node-fetch amazon-cognito-identitiy-js from npm and am trying to deploy a custom sign up lambda function.
global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const poolData = {
UserPoolId: "ap-southeast-1_******",
ClientId: "***********"
}
module.exports.router = (event, context, callback) => {
return createUser(event, context, callback);
};
function createUser(event, context, callback) {
let postBody = JSON.parse(event.body);
/*cognito test*/
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var attributeList = [];
attributeList.push(new AmazonCognitoIdentity.CognitoUserAttribute({Name:"email",Value:postBody.email}));
var cognitoResult = null;
userPool.signUp(postBody.email, 'ke$2kelmDj123', attributeList, null, function(err, result){
if (err) {
cognitoResult = err;
} else {
cognitoResult = result;
}
});
const response = {
statusCode: 201,
headers: {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
},
body: JSON.stringify({ message: "register account", special: postBody["name"], cognito: cognitoResult })
};
callback(null, response);
}
For some reason cognitoResult will only return null, even though the user is being created in my user pool in AWS.
That is because this piece of code
userPool.signUp(postBody.email, 'ke$2kelmDj123', attributeList, null, function(err, result){
if (err) {
cognitoResult = err;
} else {
cognitoResult = result;
}
});
is asynchronous and you are not waiting for the result. You just return the response while this has not yet been resolved. It resolves eventually (which you can observe by the user being created) but you have already returned from the Lambda function by then.
You can solve it by embedding the response in the else block.
userPool.signUp(postBody.email, 'ke$2kelmDj123', attributeList, null, function(err, result){
if (err) {
cognitoResult = err;
// place error response here
} else {
cognitoResult = result;
const response = {
statusCode: 201,
headers: {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
},
body: JSON.stringify({ message: "register account", special: postBody["name"], cognito: cognitoResult })
};
callback(null, response);
}
});
Note that you should also create an error response.
I'm trying to access teacher's class info using a node script. I can do it if I authenticate as the teacher themselves but I need a server connection to build a database. I'm not sure if this is a google classroom problem or if this is a bug on my end. I could be just thinking about authentication in the wrong way but I'm not sure. I am using this to authenticate:
module.exports.auth = function auth(type, callback) {
switch(type){
case "jwt":
useJwt(process.env.LXMUSER, callback)
break;
case "default":
useDefaultCred(callback);
break;
case "user":
useUser(callback);
break;
}
}
function useJwt(user, callback){
var google = require('googleapis');
var cfg = require('./config.json');
var key = require(process.env.GOOGLE_APPLICATION_CREDENTIALS);
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
cfg.scopes,
user
);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.error('Unable to authenticate with google:', err);
return
}
callback(err, jwtClient)
})
}
I added these scopes to the Oauth client manager:
classroom.courses,
classroom.coursework.students,
drive.readonly,
drive.file,
drive.metadata.readonly,
drive.appdata
I can successfully use the API call through the google drive API but not the google classroom api. Here is my calling environment:
auth("jwt", function (err, authClient) {
// Make an authorized request to list Drive files.
gclass.courses.list({
auth: authClient,
}, function (err, res) {
if (err) {
utl.errorHandle(res, err);
return;
}
if (res.courses === undefined) {
console.log(res, "You have no courses");
}
I'm trying to upload video to my YouTube channel using the googleapi module in Node.js (YouTube API V3)
The video is uploaded fine - I just can't find how to pass title and description to the upload command.
This is my code:
//Authorization stuff above
fs.readFile('./youtube_videos/in.avi', function(err, content){
if(err){
console.log('read file error: '+err);
} else {
yt.videos.insert({
part: 'status,snippet',
autoLevels: true,
media: {
body: content
}
}, function(error, data){
if(error){
console.log('error: '+error);
} else {
console.log('https://www.youtube.com/watch?v='+data.id+"\r\n\r\n");
console.log(data);
}
});
}
})
I know how should pass some snippet object like
snippet: {
title: 'test upload2',
description: 'My description2',
}
but I can't find where should it be - I tried every (almost) combination possible
Thank You!
I found the answer
In case someone is looking for it -
The snippet should be part of a resource object in the options of the request
(I also converted the fs.readFile to fs.createReadStream)
function uploadToYoutube(video_file, title, description,tokens, callback){
var google = require("googleapis"),
yt = google.youtube('v3');
var oauth2Client = new google.auth.OAuth2(clientId, appSecret, redirectUrl);
oauth2Client.setCredentials(tokens);
google.options({auth: oauth2Client});
return yt.videos.insert({
part: 'status,snippet',
resource: {
snippet: {
title: title,
description: description
},
status: {
privacyStatus: 'private' //if you want the video to be private
}
},
media: {
body: fs.createReadStream(video_file)
}
}, function(error, data){
if(error){
callback(error, null);
} else {
callback(null, data.id);
}
});
};