Bot framework in node set sequence message - botframework

Developed my bot and now I need all the messages trafficked to be sent to my API, however I have a problem: the order of the messages are not arriving sequentially, how could I solve this?
Every message sent could have an accountant, how could I do this? Or does the bot framework provide this?
Example code
https://gist.github.com/odirleiborgert/8227ff46ca8693307a5373186b9e486c
Or
"use strict"
// ----------------------------------------------------------------------
require('dotenv-extended').load()
// Import packages
const restify = require('restify')
const builder = require('botbuilder')
const api = require('./helpers/api')
// ----------------------------------------------------------------------
/**
* Bot Setup
*/
const port = process.env.port || process.env.PORT || 3978
const server = restify.createServer()
server.listen(port, () => {
console.log(`${server.name} listening to ${server.url}`)
})
const connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
})
const bot = new builder.UniversalBot(connector)
bot.use({
// Usuário envia para o bot
receive: async (event, next) => {
if (event.type == 'message' || event.type == 'conversationUpdate') {
if (process.env.API) {
await api.post.receive(event)
}
}
next()
},
// Bot envia para usuário
send: async (event, next) => {
if (event.type == 'message') {
if (process.env.API) {
await api.post.send(event)
}
}
next()
}
})
bot.use(builder.Middleware.firstRun({
version: 1.0,
dialogId: 'firstRun'
}))
bot.set('storage', new builder.MemoryBotStorage())
// ----------------------------------------------------------------------
// Provider api messages
server.post('/api/messages', connector.listen())
// ----------------------------------------------------------------------
bot.on('conversationUpdate', (session) => {
if (session.membersAdded) {
session.membersAdded.forEach((identity) => {
if (identity.id === session.address.bot.id) {
bot.beginDialog(session.address, 'firstRun')
}
})
}
})
// Add first run dialog
bot.dialog('firstRun', async (session) => {
session.userData.firstRun = true
session.delay(1000)
session.replaceDialog('start')
}).triggerAction({
onFindAction: (context, callback) => {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1)
} else {
callback(null, 0.0)
}
},
matches: [
/^Começar|Comecar$/i
]
})
// ----------------------------------------------------------------------
// Start
bot.dialog('start', require('./dialogs/start'))
.triggerAction({
matches: [
/^start|restart$/i
]
})
// ----------------------------------------------------------------------
// Hello
bot.dialog('hello', require('./dialogs/hello'))
// ----------------------------------------------------------------------
/**
* Dialogs with Intents
*/
const recognizer = new builder.LuisRecognizer(process.env.LUIS_MODEL_URL)
const intents = new builder.IntentDialog({
recognizers: [recognizer]
})
intents.onDefault(require('./dialogs/default'))
// Others
intents.matches('hello', require('./dialogs/hello'))
bot.dialog('/', intents)
// --------------------------------------------------------------------

I created a solution for the moment by creating a counter and date variable and sending the other information along to my API.
let count = 1
bot.use({
receive: async (event, next) => {
count++
if (event.type == 'message' || event.type == 'conversationUpdate') {
event.order_at = new Date()
event.sequence_at = count
await api.post.receive(event)
}
next()
},
send: async (event, next) => {
count++
if (event.type == 'message') {
event.order_at = new Date()
event.sequence_at = count
await api.post.send(event)
}
next()
}
})

Related

mqttjs keep disconnecting and reconnect in main thread, but stable in worker thread

I am using mqttjs to connect to the aws iot mqtt by wss presigned URL
In my application, there are 2 mqtt connection, the first one is from the main thread, the second one is from a web worker thread. Both are created from a same class (MqttServcie), they have the same logic, same everything.
But the one from the main thread keep disconnecting and reconnecting.
The one from the worker is very stable, the connection is never die, it never has to be reconnected.
Do you have any idea why the connection from the main thread keep disconnecting?
And what make a connection ends? (other than connection timeout and lost wifi connection)
In the image below, I end the connection and create a new one after 5 times retrying fails, so the number of request is several, nevermind.
The client id is always random, so it never be kicked off by another client.
The MqttService class
/* eslint-disable no-useless-constructor, no-empty-function */
import mqtt, { MqttClient } from 'mqtt';
import { NuxtAxiosInstance } from '#nuxtjs/axios';
import RestService from './RestService';
import { Mqtt } from '~/types/Mqtt';
import { MILISECS_PER_SEC } from '~/configs';
import { logWithTimestamp } from '~/utils';
export type MqttServiceEventHandlers = {
close?: Array<() => void>;
disconnectd?: Array<() => void>;
connected?: Array<() => void>;
reconnect?: Array<() => void>;
reconnected?: Array<() => void>;
beforeReconect?: Array<() => void>;
};
export type MqttServiceEvent = keyof MqttServiceEventHandlers;
export interface IMqttService {
httpClient?: NuxtAxiosInstance;
presignedUrl?: string;
}
export class MqttService {
public client: MqttClient | null = null;
public closing = false;
public reconnecting = false;
// Example: "wss://abcdef123-ats.iot.us-east-2.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXX%2F20230206%2Fus-east-2%2Fiotdevicegateway%2Faws4_request&X-Amz-Date=20230206T104907Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=abcxyz123"
public presignedUrl = '';
public httpClient = null as null | NuxtAxiosInstance;
public retryCount = 0;
public retryLimits = 5;
public handlers: MqttServiceEventHandlers = {};
constructor(
{ httpClient, presignedUrl }: IMqttService,
public caller = 'main'
) {
if (httpClient) {
this.httpClient = httpClient;
} else if (presignedUrl) {
this.presignedUrl = presignedUrl;
} else {
throw new Error(
'[MqttService] a httpClient or presigned URL must be provided'
);
}
}
async connect() {
await this.updatePresignedUrl();
this.client = mqtt.connect(this.presignedUrl, {
clientId: this.generateClientId(),
reconnectPeriod: 5000,
connectTimeout: 30000,
resubscribe: true,
keepalive: 15,
transformWsUrl: (_url, _options, client) => {
// eslint-disable-next-line no-param-reassign
client.options.clientId = this.generateClientId();
logWithTimestamp(
`[MQTT] [${this.caller}] transformWsUrl()`,
client.options.clientId,
this.signature
);
return this.presignedUrl;
},
});
return this.setupHandlers();
}
protected setupHandlers() {
return new Promise<MqttClient>((resolve, reject) => {
this.client!.on('close', async () => {
if (this.closing) return;
if (this.retryCount >= this.retryLimits) {
(this.handlers.close || []).forEach((handler) => handler());
await this.disconnect();
logWithTimestamp(`[MQTT] [${this.caller}] connection has closed!`);
reject(new Error(`All retry attempts were failed (${this.caller})`));
return;
}
if (this.retryCount === 0) {
(this.handlers.beforeReconect || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] connection lost`,
this.presignedUrl
);
}
// Re-generate new presigned URL at the 3rd attempt, or if the URL is expired
if (this.retryCount === 2 || this.isExpired) {
await this.updatePresignedUrl().catch(async () => {
await this.disconnect();
(this.handlers.close || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] connection has closed! (Unable to get new presigned url)`
);
});
}
});
this.client!.on('reconnect', () => {
this.retryCount += 1;
this.reconnecting = true;
(this.handlers.reconnect || []).forEach((handler) => handler());
logWithTimestamp(
`[MQTT] [${this.caller}] reconnect (#${this.retryCount})`
);
});
this.client!.on('connect', () => {
if (this.reconnecting) {
(this.handlers.reconnected || []).forEach((handler) => handler());
}
this.retryCount = 0;
this.reconnecting = false;
(this.handlers.connected || []).forEach((handler) => handler());
logWithTimestamp(`[MQTT] [${this.caller}] connected`);
resolve(this.client!);
});
});
}
disconnect({ force = true, silent = false, ...options } = {}) {
this.closing = true;
return new Promise<void>((resolve) => {
if (this.client && this.isOnline()) {
this.client.end(force, options, () => {
this.reset(silent, '(fully)');
resolve();
});
} else {
this.client?.end(force);
this.reset(silent, '(client-side only)');
resolve();
}
});
}
reset(silent = false, debug?: any) {
this.client = null;
this.retryCount = 0;
this.reconnecting = false;
this.presignedUrl = '';
this.closing = false;
if (!silent) {
(this.handlers.disconnectd || []).forEach((handler) => handler());
}
logWithTimestamp(`[MQTT] [${this.caller}] disconnected!`, {
silent,
debug,
});
}
async destroy() {
await this.disconnect({ silent: true });
this.handlers = {};
}
// Get or assign a new wss url
async updatePresignedUrl(url?: string) {
if (this.httpClient) {
const service = new RestService<Mqtt>(this.httpClient, '/mqtts');
const { data } = await service.show('wss_link');
this.presignedUrl = data!.wss_link;
} else if (url) {
this.presignedUrl = url;
}
return this.presignedUrl;
}
on(event: MqttServiceEvent, handler: () => void) {
const { [event]: eventHanlders = [] } = this.handlers;
eventHanlders.push(handler);
this.handlers[event] = eventHanlders;
}
off(event: MqttServiceEvent, handler: () => void) {
const { [event]: eventHanlders = [] } = this.handlers;
const index = eventHanlders.findIndex((_handler) => _handler === handler);
eventHanlders.splice(index, 1);
}
get date() {
const matched = this.presignedUrl.match(/(X-Amz-Date=)(\w+)/);
if (!matched) return null;
return new Date(
String(matched[2]).replace(
/^(\d{4})(\d{2})(\d{2})(T\d{2})(\d{2})(\d{2}Z)$/,
(__, YYYY, MM, DD, HH, mm, ss) => `${YYYY}-${MM}-${DD}${HH}:${mm}:${ss}`
)
);
}
get expires() {
const matched = this.presignedUrl.match(/(X-Amz-Expires=)(\d+)/);
return matched ? Number(matched[2]) : null;
}
get signature() {
const matched = this.presignedUrl.match(/(X-Amz-Signature=)(\w+)/);
return matched ? matched[2] : null;
}
get expiresDate() {
if (!(this.date && this.expires)) return null;
return new Date(this.date.getTime() + this.expires * MILISECS_PER_SEC);
}
get isExpired() {
return !this.expiresDate || this.expiresDate <= new Date();
}
private generateClientId() {
return `mqttjs_[${this.caller}]_${Math.random()
.toString(16)
.substring(2, 10)}`.toUpperCase();
}
private isOnline() {
return typeof window !== 'undefined' && window?.$nuxt?.isOnline;
}
}

Deno oak websocket must have a method that returns an async iterator

I am trying to build up a WebSocket with oak (not the native deno one).
The following code is how I build the server.
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onclose = () => { console.log('Disconnected from the client!');};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket) => {
console.log('new websocket, ws: ',ws);
const uid = globalThis.crypto.randomUUID();
sockets.set(uid,ws);
console.log('socket: ',sockets);
for await (const ev of ws){
console.log('ev: ', ev);
}
}
export const wsRoutes = new Router()
.get('/ws', runWS);
But in the for loop (at the end), for ws it says Type 'WebSocket' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.. What's the deal with this and how to fix it?
The error message is providing you with useful information: the WebSocket is not AsyncIterable, which means that it cannot be used with a for await...of loop.
Here is the type documentation for WebSocket in Deno. It is (for the most part) the same as the WHATWG standard WebSocket that is documented on MDN.
If your intention is to respond to incoming message events, you'll need to attach an event listener:
webSocket.addEventListener("message", (messageEvent) => {
// Do something in response to each message event
});
Additional:
Here's an observation based on the code you've shown, but not in response to your question:
It's probably more ergonomic to store the sockets as the keys of your map, and the associated state data in the values. (This is the inverse of what you've shown). Here's an example of why:
import {
Router,
type RouterMiddleware,
} from "https://deno.land/x/oak#v10.6.0/mod.ts";
// You seem to want to log data to the console.
// This function will help you easily log only certain properties of objects:
/**
* Functional implementation of the type utility
* [`Pick<Type, Keys>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys)
*/
function pick<T, K extends keyof T>(
obj: T,
keys: readonly K[],
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) result[key] = obj[key];
return result;
}
type SocketData = { id: string };
const socketMap = new Map<WebSocket, SocketData>();
// Do something when a connection is opened
function handleOpen(ev: Event, ws: WebSocket) {
const socketData: SocketData = { id: window.crypto.randomUUID() };
socketMap.set(ws, socketData);
console.log({
event: pick(ev, ["type"]),
socketData,
});
}
// Do something when an error occurs
function handleError(ev: Event, ws: WebSocket) {
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a connection is closed
function handleClose(ev: CloseEvent, ws: WebSocket) {
ev.code; // number
ev.reason; // string
ev.wasClean; // boolean
const socketData = socketMap.get(ws);
console.log({
event: pick(ev, ["type", "code", "reason", "wasClean"]),
socketData,
});
socketMap.delete(ws);
}
// Do something when a message is received
// Change `unknown` to the type of message payloads used in your application.
// (for example, JSON messages are `string`)
function handleMessage(ev: MessageEvent<unknown>, ws: WebSocket) {
ev.data; // unknown
ev.lastEventId; // string
ev.ports; // readonly MessagePort[]
const socketData = socketMap.get(ws);
if (socketData) {
socketData.id; // string
}
console.log({
event: pick(ev, ["type", "data", "lastEventId", "ports"]),
socketData,
});
}
const webSocketMiddleware: RouterMiddleware<"/ws"> = async (ctx, next) => {
const ws = ctx.upgrade();
ws.addEventListener("open", (ev) => handleOpen(ev, ws));
ws.addEventListener("error", (ev) => handleError(ev, ws));
ws.addEventListener("close", (ev) => handleClose(ev, ws));
ws.addEventListener("message", (ev) => handleMessage(ev, ws));
await next();
};
export const router = new Router();
router.get("/ws", webSocketMiddleware);
This is my updated code. It avoids the problem entirely
import {Application, Router, Context, send } from "https://deno.land/x/oak#v10.6.0/mod.ts";
interface BroadcastObj{
name: string,
mssg: string
}
const runWS = async (ctx: Context, next: () => Promise<unknown>) => {
if(!ctx.isUpgradable){
ctx.throw(501);
}
const uid = globalThis.crypto.randomUUID();
try{
const ws = await ctx.upgrade();
ws.onopen = () => {
chatConnection(ws);
};
ws.onmessage = (m) => {
let mssg = m.data as string;
if(typeof(mssg) === 'string'){
chatMessage(JSON.parse(mssg));
}
};
ws.onerror = (e) => {console.log('error occured: ', e);};
ws.onclose = () => { chatDisconnect(uid);};
}catch{await next();}
}
let sockets = new Map<string, WebSocket>();
const chatConnection = async (ws: WebSocket, uid: string) => {
await sockets.set(uid,ws);
}
const chatMessage = async (msg: BroadcastObj) => {
await sockets.forEach((ws: WebSocket) => {
ws.send(JSON.stringify(msg));
});
}
const chatDisconnect = async (uid: string) => {
await sockets.delete(uid);
}
export const wsRoutes = new Router()
.get('/ws', runWS);

Providing two combined Reducers for my redux saga store prevents my websocket channel message from triggering, but only one does not?

Configured my store this way with redux toolkit for sure
const rootReducer = combineReducers({
someReducer,
systemsConfigs
});
const store = return configureStore({
devTools: true,
reducer: rootReducer ,
// middleware: [middleware, logger],
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: false }).concat(middleware),
});
middleware.run(sagaRoot)
And thats my channel i am connecting to it
export function createSocketChannel(
productId: ProductId,
pair: string,
createSocket = () => new WebSocket('wss://somewebsocket')
) {
return eventChannel<SocketEvent>((emitter) => {
const socket_OrderBook = createSocket();
socket_OrderBook.addEventListener('open', () => {
emitter({
type: 'connection-established',
payload: true,
});
socket_OrderBook.send(
`subscribe-asdqwe`
);
});
socket_OrderBook.addEventListener('message', (event) => {
if (event.data?.includes('bids')) {
emitter({
type: 'message',
payload: JSON.parse(event.data),
});
//
}
});
socket_OrderBook.addEventListener('close', (event: any) => {
emitter(new SocketClosedByServer());
});
return () => {
if (socket_OrderBook.readyState === WebSocket.OPEN) {
socket_OrderBook.send(
`unsubscribe-order-book-${pair}`
);
}
if (socket_OrderBook.readyState === WebSocket.OPEN || socket_OrderBook.readyState === WebSocket.CONNECTING) {
socket_OrderBook.close();
}
};
}, buffers.expanding<SocketEvent>());
}
And here's how my saga connecting handlers looks like
export function* handleConnectingSocket(ctx: SagaContext) {
try {
const productId = yield select((state: State) => state.productId);
const requested_pair = yield select((state: State) => state.requested_pair);
if (ctx.socketChannel === null) {
ctx.socketChannel = yield call(createSocketChannel, productId, requested_pair);
}
//
const message: SocketEvent = yield take(ctx.socketChannel!);
if (message.type !== 'connection-established') {
throw new SocketUnexpectedResponseError();
}
yield put(connectedSocket());
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.BAD_CONNECTION,
})
);
}
}
export function* handleConnectedSocket(ctx: SagaContext) {
try {
while (true) {
if (ctx.socketChannel === null) {
break;
}
const events = yield flush(ctx.socketChannel);
const startedExecutingAt = performance.now();
if (Array.isArray(events)) {
const deltas = events.reduce(
(patch, event) => {
if (event.type === 'message') {
patch.bids.push(...event.payload.data?.bids);
patch.asks.push(...event.payload.data?.asks);
//
}
//
return patch;
},
{ bids: [], asks: [] } as SocketMessage
);
if (deltas.bids.length || deltas.asks.length) {
yield putResolve(receivedDeltas(deltas));
}
}
yield call(delayNextDispatch, startedExecutingAt);
}
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.UNKNOWN,
})
);
}
}
After Debugging I got the following:
The Thing is that when I Provide one Reducer to my store the channel works well and data is fetched where as when providing combinedReducers I am getting
an established connection from my handleConnectingSocket generator function
and an empty event array [] from
const events = yield flush(ctx.socketChannel) written in handleConnectedSocket
Tried to clarify as much as possible
ok so I start refactoring my typescript by changing the types, then saw all the places that break, there was a problem in my sagas.tsx.
Ping me if someone faced such an issue in the future

sendResponse in Port.postmessage()

I have the following code
browser.runtime.onConnect.addListener(function (externalPort) {
externalPort.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(42);
}
});
However, it seems that listeners for Port.onMessage do not get called with a sendResponse as listeners for browser.runtime.onMessage.
Any idea how to send responses for messages to ports?
Port-based messaging doesn't use sendResponse. Simply post another message to the port.
Here's a very simplified example of a port-based messaging system. It doesn't transfer errors or exceptions, doesn't have a timeout. The idea is to pass an id, save the callback for the id in a map, and use the same id in the response to call that saved callback.
Unlike browser.runtime.sendMessage that creates a new port each time (a relatively expensive operation in case you send a lot of messages), we reuse the same port.
sender:
const port = browser.runtime.connect({name: 'foo'});
const portMap = new Map();
let portMessageId = 0;
port.onMessage.addListener(msg => {
const {id, data} = msg;
const resolve = portMap.get(id);
portMap.delete(id);
resolve(data);
});
function send(data) {
return new Promise(resolve => {
const id = ++portMessageId;
portMap.set(id, resolve);
port.postMessage({id, data});
});
}
usage:
(async () => {
const response = await send({foo: 'whatever'});
console.log(response);
})();
receiver:
/** #param {chrome.runtime.Port} port */
browser.runtime.onConnect.addListener(port => {
if (port.name === 'foo') {
port.onMessage.addListener(msg => {
const {id, data} = msg;
port.postMessage({id, data: processMessage(data)});
});
}
});
The Port.postMessage() is a push-only messaging method, so you need to use regular runtime.sendMessage() method in parallel. Here is an example:
manifest.json:
{
"name": "panel example",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "panel",
"default_popup": "panel.html"
},
"permissions": [
"tabs"
]
}
background.js:
browser.runtime.onConnect.addListener(port => {
let tabId;
const listenerForPort = (message, sender) => {
if (message &&
typeof message == 'object' &&
message.portName == port.name) {
switch (message.type) {
case 'get-tabId':
return Promise.resolve(tabId);
}
}
};
browser.runtime.onMessage.addListener(listenerForPort);
port.onMessage.addListener(message => {
if (message &&
typeof message == 'object' &&
message.tabId)
tabId = message.tabId;
});
port.onDisconnect.addListener(port => {
browser.runtime.onMessage.removeListener(listenerForPort);
if (tabId)
browser.tabs.remove(tabId);
});
});
panel.html:
<!DOCTYPE html>
<script type="application/javascript" src="panel.js"></script>
<button id="button">Click Me</button>
panel.js:
browser.windows.getCurrent({ populate: true }).then(win => {
const portName = `port for window ${win.id}`;
const activeTab = win.tabs.find(tab => tab.active);
const port = browser.runtime.connect({
name: portName
});
port.postMessage({ tabId: activeTab.id });
const button = document.getElementById('button');
button.addEventListener('click', async event => {
const tabIdFromBackground = await browser.runtime.sendMessage({
type: 'get-tabId',
portName
});
button.textContent = tabIdFromBackground;
});
});
In this example, there is a listener corresponding to a connection and it is designed to respond only to messages sent with the corresponding port name.

How come drive API return a result when using invalid access token

My Scenario
I'm using Google Drive API to create a file and to get a list of files.
My problem
1. No matter what value I put in my access_token the API keeps working
2. If I change the order of events and I call createDriveFile before I call listDriveFiles I get this error:
Error: Invalid Credentials
at Gaxios._request (/Users/tamirklein/superquery/bd/lambda/node_modules/googleapis-common/node_modules/google-auth-library/node_modules/gaxios/src/gaxios.ts:109:15)
at
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
My code
if (!global._babelPolyfill) {
var a = require("babel-polyfill")
}
import {google} from 'googleapis'
describe('Run query with API', async () => {
it('check Drive APIs', async () => {
process.env.x_region = 'us-east-1';
let result = await test('start')
})
async function test(p1) {
let auth = getBasicAuthObj();
auth.setCredentials({
access_token: "anyValueWork",
refresh_token: "Replace With a valid refresh Token"
});
let fileList = await listDriveFiles(auth);
let newFile = await createDriveFile(auth);
}
async function listDriveFiles(auth) {
return new Promise((resolved) => {
const {google} = require('googleapis');
const drive = google.drive({version: 'v3', auth});
drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
q: 'trashed=false'
}, (err, res) => {
if (err) {
console.log('The API returned an error: ' + err);
resolved([err, null]);
} else {
const files = res.data.files;
if (files.length) {
console.log(`We fetched ${files.length} Files`);
// files.map((file) => {
// console.log(`${file.name} (${file.id})`);
// });
} else {
console.log('No files found.');
}
resolved([err, res]);
}
});
});
}
async function createDriveFile(auth) {
return new Promise(async (resolved) => {
//const fs = require('fs');
const {google} = require('googleapis');
const drive = google.drive({version: 'v3', auth});
let data = {
value: 'value'
};
let fileName = 'fileName.txt';
let fileMetadata = {
'name': fileName
};
// create buffer
let stream = require('stream');
let bufferStream = new stream.PassThrough();
bufferStream.end(Buffer.from(JSON.stringify(data)));
let media = {
mimeType: 'application/json',
body: bufferStream // fs.createReadStream("test.txt") //bufferStream //
};
drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error("Error: savePasswordInDrive" + err);
} else {
console.log('File Id: ', file.data.id);
}
resolved([err, file]);
});
})
}
async function _wait(milliseconds) {
return new Promise(resolved => {
setTimeout(() => {
resolved()
}, milliseconds)
})
}
/**
* Create oAuth object
* #returns {OAuth2Client}
*/
function getBasicAuthObj() {
let clientId = 'Replace With a valid clientId';
let clientSecret = 'Replace With a valid clientSecret';
let redirectUrl = 'URL';
return new google.auth.OAuth2(
clientId,
clientSecret,
redirectUrl
)
}
})
Any ideas on how to resolve this?

Resources