How to use Google PubSub without Cloud Function - node.js

How can I use Google PubSub to retrieve billing updates without using cloud functions? I am using the code below currently but it says that onPublish does not exist:
const { PubSub } = require('#google-cloud/pubsub');
const pubsub = new PubSub('MyProjectID');
handleServerEvent = pubsub.topic(GOOGLE_PLAY_PUBSUB_BILLING_TOPIC)
.onPublish(async (message) => {
})
TypeError: pubsub.topic(...).onPublish is not a function
I am using Node.js and want to react to events published on a topic.

The onPublish() method is a part of Cloud Functions API. You need to use createSubscription() to get a Subscription object and then use it to listen for new messages. Try the following:
const listenToTopic = async (topicName: string) => {
const [sub] = await pubsub
.topic(topicName)
.createSubscription("subscriptionName");
sub.on("message", (message) => {
message.ack();
console.log(`Received message: ${message}`);
});
};
// start listener
listenToTopic(GOOGLE_PLAY_PUBSUB_BILLING_TOPIC)
After creating the subscription once you need to change createSubscription("subscriptionName") to subscription("subscriptionName") to listen to incoming messages as the subscription has been created already

Related

Function execution took 540029 ms, finished with status: 'timeout'

I have created a cloud function that connects to an MQTT broker I have used a third-party MQTT broker (Mosquito MQTT broker), and sends the data to the Firebase real-time database every time the MQTT broker receives data from the machine. I am using the GCP console for writing and deploying the function. I successfully deployed the function without any errors, however, when I test it from the GCP console, it starts sending data but stops after the time specified in the timeout. I have tried timeout values from 60 to 540 seconds, but it still stops after the specified time. I have also increased the allocated memory, but it hasn't resolved the issue and I keep getting the same timeout error.
This is my code
const Admin = require("firebase-admin");
const functions = require("firebase-functions");
const mqtt = require('mqtt');
const clientId = 'mqtt_googleserver_********7'
const topic = '#'
const serviceAccount = require("./service.json");
Admin.initializeApp({
credential: Admin.credential.cert(serviceAccount),
databaseURL: "https://***************firebaseio.com/"
});
exports.rtdb_mains = functions.https.onRequest((_request, _response) => {
const client = mqtt.connect('mqtt://**.**.**.****.***',{
clientId,
clean: true,
connectTimeout: 4000,
username: '******',
password: '********',
reconnectPeriod: 1000,
});
const db = Admin.database();
client.addListener('connect', () => {
console.log('Connected');
client.subscribe([topic], { qos: 1 });
console.log(`Subscribe to topic '${topic}'`);
});
client.on('message', async (topic, payload) => {
console.log('Received Message:', topic, payload.toString());
if (payload.toString() !== "" && topic !== "") {
const ref = db.ref("All_machines");
const childref = ref.child(topic.toString());
await childref.set(payload.toString());
const topicDetails = topic.split("/");
const machineId = topicDetails[1];
const machineParameter = topicDetails[2];
if (machineParameter === "BoardID") {
const ref = db.ref(machineParameter);
await ref.set(machineId);
}
}
});
});
can anyone please help me with this problem.
You don't need to specify a service.json if you push the CF on firebase. You can directly use the default configuration.
You can do directly this :
admin.initializeApp();
Secondly, the way you use your MQTT implementation and the cloud function are not correct.
You are listenning and waiting for a message in a function that is trigger only by a POST or GET request.
I suggest to use the pub/sub api for doing such a thing and have a good implementation for sending / receiving messages.
In case of you really need to listen for message in your MQTT implementation, you will need another provider than Cloud Function or calling the native MQTT of Cloud Function
https://cloud.google.com/functions/docs/calling/pubsub
https://www.googlecloudcommunity.com/gc/Serverless/Can-a-cloud-function-subscribe-to-an-MQTT-topic/m-p/402965

Getting multiple 404 and 405 errors in Teams Bot

I'm doing some investigating around a Teams Bot that I currently have in development. I'm seeing a lot of 404, and in some other cases, 405 errors when I look in Application Insights - I'm trying to understand if I've missed anything.
I have the App Service set to 'Always On' so my assumption is that it's polling the service every 5 minutes to keep it from idling out. However, I'm seeing a lot of 404 failures, specifically pointing to the GET/ endpoint and in other cases a 405 error as well, which is pointing to the api/messages endpoint.
I have the App ID and App Password set in the environment variables and I've set the storage using a Cosmos DB too as shown in the index.js file below. I have also checked the Teams manifest to ensure it's pointing to the Bot ID and recently added the bot domain as well to see if that makes a difference.
const restify = require('restify');
const path = require('path');
// Import required bot services.
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
const { BotFrameworkAdapter, ConversationState, UserState } = require('botbuilder');
// Import required services for bot telemetry
const { ApplicationInsightsTelemetryClient, TelemetryInitializerMiddleware } = require('botbuilder-applicationinsights');
const { TelemetryLoggerMiddleware, NullTelemetryClient } = require('botbuilder-core');
// Import our custom bot class that provides a turn handling function.
const { DialogBot } = require('./bots/dialogBot');
const { ProvisioningProfileDialog } = require('./dialogs/provisioningProfileDialog');
// Read environment variables from .env file
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create the adapter. See https://aka.ms/about-bot-adapter to learn more about using information from
// the .bot file when configuring your adapter.
const adapter = new BotFrameworkAdapter({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Define the state store for your bot.
const { CosmosDbPartitionedStorage } = require('botbuilder-azure');
const cosmosStorage = new CosmosDbPartitionedStorage({
cosmosDbEndpoint: process.env.CosmosDbEndpoint,
authKey: process.env.CosmosDbAuthKey,
databaseId: process.env.CosmosDbDatabaseId,
containerId: process.env.CosmosDbContainerId,
compatibilityMode: false
});
// Create conversation state with storage provider.
const conversationState = new ConversationState(cosmosStorage);
const userState = new UserState(cosmosStorage);
// Create the main dialog.
const dialog = new ProvisioningProfileDialog(userState);
const bot = new DialogBot(conversationState, userState, dialog);
dialog.telemetryClient = telemetryClient;
// Catch-all for errors.
const onTurnErrorHandler = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
// Send a trace activity, which will be displayed in Bot Framework Emulator
await context.sendTraceActivity(
'OnTurnError Trace',
`${ error }`,
'https://www.botframework.com/schemas/error',
'TurnError'
);
// Send a message to the user
await context.sendActivity('The bot encountered an error or bug.');
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
// Clear out state
await conversationState.delete(context);
};
// Set the onTurnError for the singleton BotFrameworkAdapter.
adapter.onTurnError = onTurnErrorHandler;
// Add telemetry middleware to the adapter middleware pipeline
var telemetryClient = getTelemetryClient(process.env.InstrumentationKey);
var telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(telemetryClient);
var initializerMiddleware = new TelemetryInitializerMiddleware(telemetryLoggerMiddleware);
adapter.use(initializerMiddleware);
// Creates a new TelemetryClient based on a instrumentation key
function getTelemetryClient(instrumentationKey) {
if (instrumentationKey) {
return new ApplicationInsightsTelemetryClient(instrumentationKey);
}
return new NullTelemetryClient();
}
// Create HTTP server.
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }.`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
// Route the message to the bot's main handler.
await bot.run(context);
});
});
Whilst the Bot appears to run okay for the most part, am I missing something with these errors or is this expected behaviour since it's polling for a response?
Thanks in advance
Does your bot contain a web page as well? The node samples do not, but .NET samples do. If not, it would make sense, of course, to receive a 404. I tend to agree with you that the polling might be the cause.
Bots typically (especially when created from template or sample), do not handle GET endpoints to /api/messages. Everything is handled using POST.

How to get AWS SQS queue ARN in nodeJS?

I'm trying to build an application with a basic client-server infrastructure. The server infrastructure is hosted on AWS, and when a client logs on, it sends a message to the server to set up various infrastructure considerations. One of the pieces of infrastructure is an SQS Queue that the client can poll from to get updates from the server (eventually I'd like to build a push service but I don't know how for right now).
I'm building this application in NodeJS using the Node AWS SDK. The problem I'm having is I need the queue ARN to do various things like subscribe the SQS queue to an SNS topic that the application uses, but the create queue API returns the queue URL, not ARN. So I can get the ARN from the URL using the getQueueAttributes API, but it doesn't seem to be working. Whenever I call it, I get undefined as the response. Here's my code, please tell me what I'm doing wrong:
exports.handler = (event, context, callback) => {
new aws.SQS({apiVersion: '2012-11-05'}).createQueue({
QueueName: event.userId
}).promise()
)
.then(data => { /* This has the Queue URL */
new aws.SQS({apiVersion: '2012-11-05'}).getQueueAttributes({
QueueUrl: data.QueueUrl,
AttributeNames: ['QueueArn']
}).promise()
})
.then(data => {
console.log(JSON.stringify(data)); /* prints "undefined" */
})
/* Some more code down here that's irrelevant */
}
Thanks!
const AWS = require('aws-sdk');
const sqs = new AWS.SQS();
exports.handler = async(event, context, callback) => {
var params = {
QueueUrl: 'my-queue-url',
AttributeNames: ['QueueArn']
};
let fo = await sqs.getQueueAttributes(params).promise();
console.log(fo);
};
and it printed
{
ResponseMetadata: { RequestId: '123456-1234-1234-1234-12345' },
Attributes: {
QueueArn: 'arn:aws:sqs:eu-west-1:12345:my-queue-name'
}
}
With the help of Ersoy, I realized that I was using block-formatting (with {}) to write my Promises, but I was never returning anything from those blocks. I had thought that the last value in the Promise block was the return value by default, but it seems that was not the case. When I added return before the SQS API command, then it worked (without using async/await).

Nodejs Telegram Bot: sendChatAction while waiting for network call

I am writing a Telegram bot using the Telegraf framework in Nodejs, and would like to display the 'bot is typing...' banner while the bot is performing a networking call.
The issue that I have is that my function isn't executed like how it is supposed to be, ie the 'typing' banner appears and disappears after 5 seconds (as per the docs), but the invoice never gets sent until at least 30 seconds later. Assessing the logs also shows that the console logs were executed after the function ends.
Here is my implementation:
module.exports = (bot) => bot.command('buy', (ctx) => {
let catalogueId = //some regex
return Promise.all([
ctx.replyWithChatAction('typing'),
sendInvoice(catalogueId, ctx)
])
})
function sendInvoice(catalogueId, ctx) {
console.log('1')
return helper.getItem(catalogueId)
.then((item) => {
console.log('2')
return createInvoice(item, ctx)
.then((invoice) => {
console.log('3')
return ctx.replyWithInvoice(invoice)
})
})
}
The log statements as seen in my Firebase Cloud Functions are like so:
As you can see, the console logs are printed after the function has ended, and are at almost 15 seconds apart. Other attempts:
//Attempt 2, same result as above
module.exports = (bot) => bot.command('buy', (ctx) => {
let catalogueId = //some regex
return ctx.replyWithChatAction('typing')
.then(() => sendInvoice(catalogueId, ctx))
})
//Attempt 3, log statements are printed before function ends, but the 'typing' only shows at the end. Need it to show when the networking calls starts
module.exports = (bot) => bot.command('buy', (ctx) => {
let catalogueId = //some regex
return sendInvoice(catalogueId, ctx))
})
function sendInvoice(catalogueId, ctx) {
console.log('1')
return helper.getItem(catalogueId)
.then((item) => {
console.log('2')
return createInvoice(item, ctx)
.then((invoice) => {
console.log('3')
return ctx.replyWithChatAction('typing')
.then(() => ctx.replyWithInvoice(invoice))
})
})
}
My bot is currently deployed on Firebase Cloud Function.
I had a similar problem with a bot running on AWS Lambda. My bot would send the typing chat action correctly to the chat, then all of a sudden the lambda function would quit and none of my other code would be processed.
I'm not an expert but I think that as soon as the handler function you provide to Firebase Cloud Function (or Lambda) returns something to the client that initiated your request, Firebase considers the request complete and shuts down your function invocation. The default webhook callback that Telegraf provides sends a response to the client when your bot replies with a chat action or a message. Consequently, if you export that default webhook reply as your handler (as I did) then your handler will return a response as soon as your bot replies with anything, including a chat action, and the rest of your code may not be processed.
I solved my problem by replacing this:
export const handler = makeHandler(bot.webhookCallback("/"))
with this:
export const handler = async (event, context, callback) => {
const tmp = JSON.parse(event.body) // get data passed to us
await bot.handleUpdate(tmp) // make Telegraf process that data
return callback(null, {
// return something for webhook, so it doesn't try to send same stuff again
statusCode: 200,
body: "",
})
}
(copied from this excellent github comment)
As you can see, instead of returning whenever the bot's webhook callback returns something, I'm now waiting until the update is completely handled and only returning afterward. Thus I can reply with a chat action, wait for my code to do some processing, reply with a message, wrap things up, and only then will my function invocation stop processing.

How to pass on an updated variable outside of MQTT event to use it otherwise as condition? (Hyperledger Fabric & MQTT)

First, we are quite new in the area Hyperledger Fabric and MQTT. We are building a prototype with a Blockchain platform (Hyperledger Fabric) written in nodeJS and a raspberry pi for transmitting the IoT data (working with mqtt). In our nodeJS file we create a MQTT client that subscribes to a topic and gets as a message a JSON object. The parameters of that object have to be passed on to the smart contract (submitContract). We realized that the gateway for Hyperledger Fabric disconnects before it runs the MQTT event functions (client.on) and therefore we thought we let the gateway disconnect on the condition of a submitted contract (see below code). In order to do so we want to get the success variable out of the client.on("message") function so that we can use it for the if statement at the end. However, the success variable doesn't seem to get updated to success=true by simply using return within client.on() so that eventually the program never exits (aka gateway is not disconnected). Below, you see parts of the code (the relevant parts). Does anyone know how to pass on properly an updated variable out of the MQTT event function?
'use strict';
const { FileSystemWallet, Gateway } = require('fabric-network');
const mqtt = require("mqtt");
async function main() {
try {
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('fabcar');
//MQTT client connection & contract submission
console.log("connecting to broker");
const client = mqtt.connect("mqtt://192.168.43.217");
var success = false;
client.on("connect", () =>{
console.log("Subscribing");
client.subscribe("rfidData");
console.log("Please hold your tag on the RFID reader. Wait...");
});
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
success = true;
console.log("Success? " + success);
return success;
});
client.stream.on('error', (err) => {
console.log('errorMessage', err);
client.end()
});
client.on("offline",() =>{
console.log("offline");
});
client.on("reconnect", ()=>{
console.log("reconnect");
});
// Disconnect from the gateway.
if (success === true){
client.end();
gateway.disconnect();
};
} catch (error) {
console.error(`Failed to submit transaction: ${error}`);
process.exit(1);
}
}
main();
You need to take on the concept of asynchronous execution and event driven systems. The application you have written does not execute in the order it is written.
The on('message',....) call back will only be executed when a message is delivered to client. This function also does not return any values (it is not actually called by any of your code, it is called by the underlying MQTT client code) so it will not "return" the value of success.
If you want the application to disconnect after it has received the first message then the easiest thing to do is to move the code that disconnects the MQTT client and the Fabric gateway to inside the callback. e.g.
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});
EDIT:
Looking at the doc it appears that submitTransaction() is flagged as being an async function, so you should be able to use await to block until it completes
...
console.log(rfidPayload);
await contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});

Resources