How to create a shared drive with Google Drive API (node.js) - google-api

I'm trying to make a script that makes a shared drive from the google drive api but right now I'm getting an error inside the createSharedDrive(auth) function where it says
drive.drives.create
as it's saying the 'create' function is undefined. Not sure how to fix it. The google drive api documentation doesn't show the entire script to create a shared drive.
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const { uuid } = require('uuidv4');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
//authorize(JSON.parse(content), listFiles);
authorize(JSON.parse(content), createSharedDrive);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
resource: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}

The create method does not contains any "resource" property. It contains a requestId property and a requestBody property; Therefore, your request should look like this:
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
requestBody: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}

Related

How should a botframework webchat conversation be maintained for over an hour?

I have looked through the documentation for botframework-webchat and have not been able to find any documentation on how conversations over 1 hour should be handled properly. This situation is most likely to occur if a web page is left idle in the background for an extended period of time.
The directline connection is maintained as long as the webchat remains active on a web page. The problem occurs after a page refresh.
The initial short term solution is to store the relevant conversation information in session storage, such as a token. The problem is that the token for the conversation is refreshed every 15 minutes. The refreshed token must be retrieved in order to maintain the conversation upon a page refresh.
I am sure a hacky work around exists for retrieving the refreshed token from the directline client object using an event callback.
Ideally, I am looking for a clean framework designed approach for handling this situation.
Though a working solution is better than no solution.
Relevant Link:
https://github.com/microsoft/BotFramework-WebChat
Thanks.
You can achieve this by implementing cookies in your client side. you can set cookies expiration time to 60 min and you can use watermark to make your chat persistent for one hour.
Passing cookie to and from Bot Service.
You can achieve this by setting up a "token" server. In the example below, I run this locally when I am developing/testing my bot.
You can use whatever package you want, however I landed on "restify" because I include it in the index.js file of my bot. I simply create a new server, separate from the bot's server, and assign it a port of it's own. Then, when I run the bot it runs automatically, as well. Put your appIds, appPasswords, and secrets in a .env file.
Then, in your web page that's hosting your bot, simply call the endpoint to fetch a token. You'll also notice that the code checks if a token already exists. If so, then it set's an interval with a timer for refreshing the token. The interval, at 1500000 ms, is set to run before the token would otherwise expire (1800000 ms). As such, the token is always getting refreshed. (Just popped in my head: may be smart to log the time remaining and the amount of time that passed, if the user navigated away, in order to set the interval to an accurate number so it refreshes when it should. Otherwise, the interval will reset with the expiration time being something much less.)
Also, I included some commented out code. This is if you want your conversations to persist beyond page refreshes or the user navigating away and returning. This way current conversations aren't lost and the token remains live. May not be necessary depending on your needs, but works well with the above.
Hope of help!
Token Server
/**
* Creates token server
*/
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
const corsToken = corsMiddleware({
origins: [ '*' ]
});
// Create HTTP server.
let server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.listen(process.env.port || process.env.PORT || 3500, function() {
console.log(`\n${ server.name } listening to ${ server.url }.`);
});
// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${ process.env.directLineSecret }`
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
// response.statusCode = 400;
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone requested a token...');
} else if (response.statusCode === 400) {
res.send(400);
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
// Listen for incoming requests.
server.post('/directline/refresh', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/refresh',
headers: {
'Authorization': `Bearer ${ req.body.token }`,
'Content-Type': 'application/json'
},
json: {
user: {
ID: userId
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone refreshed a token...');
} else {
res.status(500);
res.send('Call to retrieve token from DirectLine failed');
}
});
});
webchat.html
<script>
(async function () {
let { token, conversationId } = sessionStorage;
[...]
if ( !token || errorCode === "TokenExpired" ) {
let res = await fetch( 'http://localhost:3500/directline/token', { method: 'POST' } );
const { token: directLineToken, conversationId, error } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}
if (token) {
await setInterval(async () => {
let res = await fetch( 'http://localhost:3500/directline/refresh', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( { token: token } )
} );
const { token: directLineToken, conversationId } = await res.json();
// sessionStorage[ 'token' ] = directLineToken;
// sessionStorage[ 'conversationId' ] = conversationId;
token = directLineToken;
}, 1500000)
}
// if ( conversationId ) {
// let res = await fetch( `https://webchat.botframework.com/v3/directline/conversations/${ conversationId }`, {
// method: 'GET',
// headers: {
// 'Authorization': `Bearer ${ token }`,
// 'Content-Type': 'application/json'
// },
// } );
// const { conversationId: conversation_Id, error } = await res.json();
// if(error) {
// console.log(error.code)
// errorCode = error.code;
// }
// conversationId = conversation_Id;
// }
[...]
window.ReactDOM.render(
<ReactWebChat
directLine={ window.WebChat.createDirectLine({ token });
/>
),
document.getElementById( 'webchat' );
});
</script>
The solution involved storing the conversation id in session storage instead of the token. Upon a page refresh a new token will be retrieved.
https://github.com/microsoft/BotFramework-WebChat/issues/2899
https://github.com/microsoft/BotFramework-WebChat/issues/2396#issuecomment-530931579
This solution works but it is not optimal. A better solution would be to retrieve the active token in the directline object and store it in session storage. The problem is that a way to cleanly way to retrieve a refreshed token from a directline object does not exist at this point.

Heroku app crashes when I try to post an image to server from client

I've successfully deployed a CRUD app on Heroku. And everything works fine on the deployed web app until I send a POST request to Heroku to post a picture to the server that then sends to S3. Everything works fine, including the picture post request, locally. However I get the following error message when I hit the deployed heroku server.
POST https://backend.herokuapp.com/ 503 (Service Unavailable)
Access to fetch at 'https://backend.herokuapp.com/' from origin 'https://frontend.netlify.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
bundle.esm.js:63 Uncaught (in promise) Error: Network error: Failed to fetch
at new t (bundle.esm.js:63)
at Object.error (bundle.esm.js:1030)
at g (Observable.js:140)
at O (Observable.js:179)
at e.value (Observable.js:240)
at bundle.esm.js:869
at Set.forEach (<anonymous>)
at Object.error (bundle.esm.js:869)
at g (Observable.js:140)
at O (Observable.js:179)
This is my code to save the POSTed picture on the server, send it to S3, and then delete the photo on the server.
import * as shortid from "shortid";
import { createWriteStream, createReadStream, unlinkSync } from "fs";
const aws = require("aws-sdk");
aws.config.update({
accessKeyId: process.env.AWS_accessKeyId,
secretAccessKey: process.env.AWS_secretAccessKey
});
const BUCKET_NAME = "dormsurf";
const s3 = new aws.S3();
const storeUpload = async (stream: any, mimetype: string): Promise<any> => {
// aseq2
const extension = mimetype.split("/")[1];
console.log("extension: ", extension);
const id = `${shortid.generate()}.${extension}`;
const path = `src/images/${id}`;
console.log("path", path);
return new Promise((resolve, reject) =>
stream
.pipe(createWriteStream(path))
.on("finish", () => resolve({ id, path }))
.on("error", reject)
);
};
export const processUpload = async (upload: any) => {
const { stream, mimetype } = await upload;
const { id } = await storeUpload(stream, mimetype);
console.log("id");
console.log(id);
var params = {
Bucket: BUCKET_NAME,
Key: `listings_images/${id}`,
Body: createReadStream(`src/images/${id}`)
};
s3.upload(params, function(err, data) {
if (err) {
console.log("error in callback");
console.log(err);
}
console.log("success");
console.log(data);
try {
unlinkSync(`src/images/${id}`);
//file removed
} catch (err) {
console.error(err);
}
});
return id;
};
Thank you so much for the help!
this error is a CORS error fix this error using proxy
call you backend api using this proxy https://cors-anywhere.herokuapp.com/
in your front call api like this
https://cors-anywhere.herokuapp.com/https://backend.herokuapp.com/

use async/await instead of (.then)

I am trying to replace .then with async/await
here is my code
export const loginUser = (userData) => async dispatch => {
try {
await axios.post('/api/users/login', userData);
// Save to localStorage
const {token} = res.data;
// Save token to local storage
localStorage.setItem('jwtToken', token);
// Set token to Auth header
setAuthToken(token);
// Decode to get user data
const decoded = jwt_decode(token);
// set current user
dispatch(setCurrentUser(decoded));
} catch (err) {
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
}
};
and then this is the error
Unhandled Rejection (TypeError): Cannot read property 'data' of
undefined ▶ 6 stack frames were collapsed.

How to publish to iot stream from lambda

I keep having a timeout in my lambda function when I try to call the iotData publish function. Code below. It always times out without error. This function works from the sam local command line. I imagine this is a permissions error with lambda. The strange thing is I've given permissions for IoT, Kinesis and SNS already to this lambda function but nothing is working.
'use strict';
console.log('Loading function');
require('dotenv').config();
const {Pool} = require('pg');
const pool = new Pool();
const AWS = require('aws-sdk');
console.log("finished loading");
/** * Provide an event that contains the following keys:
* * * - resource: API Gateway resource for event
* * - path: path of the HTTPS request to the microservices API call
* * - httpMethod: HTTP method of the HTTPS request from microservices API call
* * - headers: HTTP headers for the HTTPS request from microservices API call
* * - queryStringParameters: query parameters of the HTTPS request from microservices API call
* * - pathParameters: path parameters of the HTTPS request from microservices API call
* * - stageVariables: API Gateway stage variables, if applicable
* * - body: body of the HTTPS request from the microservices API call
* */
exports.handler = function(event, context, callback) {
console.log("starting");
let _response = "";
context.callbackWaitsForEmptyEventLoop = false;
if(event.httpMethod==="POST" && event.resource==="/pings"){
var body = JSON.parse(event.body);
console.log("here2");
pool.query("SELECT name from pings where test = decode($1,'hex');",[body.bid], (err,res)=>{
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response, null);
}
console.log("here3");
var iotdata = new AWS.IotData({endpoint:'XXXXXXX.iot.us-east-1.amazonaws.com'});
const publishParams = {
topic: body.topic,
payload: Buffer.from(JSON.stringify({
message: "Welcome to "+res.rows[0].name+" house"
}), 'utf8'),
qos: 0
}
console.log("here4");
iotdata.publish(publishParams, function(err, data) {
if(err){
console.error(err.stack);
_response = buildOutput(500, {
message:"error in pg"
});
callback(_response,null);
}
_response = buildOutput(200, {message: "success"});
callback(null, _response);
});
});
} else {
_response = buildOutput(500, {
message:"path not found"
});
callback(_response,null);
}
};
/* Utility function to build HTTP response for the microservices output */
function buildOutput(statusCode, data) {
let _response = {
statusCode: statusCode,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(data)
};
return _response;
}
policy
{
"Sid": "",
"Effect": "Allow",
"Action": [
"iot:*"
],
"Resource": "*"
},
UPDATE:
I attempted to give the lambda function admin access temporarily and that did not even work.
This is how you can post on MQTT topic directly from lambda function.
Use the code written in Node.js 10.x
var AWS = require('aws-sdk');
const IOT_ENDPOINT = "yourIoTentPoint.iot.region.amazonaws.com"
var iotdata = new AWS.IotData({endpoint:IOT_ENDPOINT});
exports.handler = function(event, context, callback) {
var params = {
topic: 'my/topic',
payload: 'This is my menssage from Lambda function.',
qos: 1
};
iotdata.publish(params, function(err, data){
if(err){
callback(err, null);
}
else{
callback(null, {"published_message": params.payload, "topic": params.topic});
}
});
};
Also, you can check the message sent by subscribing on the topic my/topic through IoT Core>Test on AWS Console.

node.js(Javascript) google-api-nodejs-client - list messages

My question was voted down, so i am rewriting it hopefully this is more succinct
I am stuck at writing a javascript function to list messages in inbox.
Using - official "google-api-nodejs-client", node.js, electron, (and javascript)
Goal: list messages in gmail inbox
For that to work i need to authorize first then ask for the messages
Authorize
- I copied the code from google node.js quickstart
- this works in electron (well actually node.js as it is a command line script).
Ask for Messages
- Google has an example, i copied it adjusted some parts but doesn't work. I think the example i am working from is not designed for the node.js "google-api-nodejs-client".
- Maybe it needs a different authorize
This is the listmessages function from the google example, i can't seem to figure out how to make this work with the authorize from list labels. This is what i have tried
changing gapi to google
changing userId to 'me'
changing givig it a query
Does not use a client library.
Does not use a client library.
/**
* Retrieve Messages in user's mailbox matching query.
*
* #param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* #param {String} query String used to filter the Messages listed.
* #param {Function} callback Function to call when the request is complete.
*/
function listMessages(userId, query, callback) {
var getPageOfMessages = function(request, result) {
request.execute(function(resp) {
result = result.concat(resp.messages);
var nextPageToken = resp.nextPageToken;
if (nextPageToken) {
request = gapi.client.gmail.users.messages.list({
'userId': userId,
'pageToken': nextPageToken,
'q': query
});
getPageOfMessages(request, result);
} else {
callback(result);
}
});
};
var initialRequest = gapi.client.gmail.users.messages.list({
'userId': userId,
'q': query
});
getPageOfMessages(initialRequest, []);
}
This is the aurhorize function that works to list labels.
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), listLabels);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
/**
* Lists the labels in the user's account.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listLabels(auth) {
var gmail = google.gmail('v1');
gmail.users.labels.list({
auth: auth,
userId: 'me',
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var labels = response.labels;
if (labels.length == 0) {
console.log('No labels found.');
} else {
console.log('Labels:');
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
console.log('- %s', label.name);
}
}
});
}
The flow of your program should look like this:
Then you can use the api to list messages. (just to get you started):
export function listMessages(oauth2Client, userId, query, callback) {
const gmail = google.gmail('v1');
gmail.users.messages.list({
auth: oauth2Client,
userId: 'me',
}, (err, response) => {
console.log(response);
});
}
Notice the oauth2Client parameter. This is the object you get from your function authorize. Let me know if you have any questions.

Resources