I have made a script for sending push notifications to my Firebase server, but javascript eslint is throwing error for const first.
Then I found on Google that I have to put ecmaVersion = 6 in my .eslintsrc file.
I did that then it is showing error on require, exports and console field.
I am using Atom as my compiler for code. This is my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.printUrl = functions.database.ref('images/{uid}').onWrite(event => {
var request = event.data.val();
var payload = {
data:{
url : request.url,
location : request.location
}
};
admin.messaging().sendToTopic(request.topic, payload)
.then(function(response){
console.log("Successfully sent message : ", response);
})
.catch(function(error){
console.log("Error sending message : ", error);
})
});
You need to let eslint know that you're working in a Node environment to get rid of the require and exports errors. So by adding this to your eslintConfig:
"env": {
"node": true
}
In order to allow the console.log, you will have to turn on the rule by adding this to your eslintconfig:
"rules": {
"no-console": 0
}
You can find more information here: https://eslint.org/docs/user-guide/configuring
Related
We operate bots by combining Firebase and Slack/bolt.
I currently use functions.config()to manage my Slack tokens and secrets, but would like to migrate to using Secret Manager.
https://firebase.google.com/docs/functions/config-env#secret-manager
boltapp.js
const functions = require("firebase-functions");
const { App, ExpressReceiver, subtype } = require("#slack/bolt");
const config = functions.config();
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SECRET,
endpoints: "/events",
processBeforeResponse: true,
});
const app = new App({
receiver: expressReceiver,
token: process.env.SLACK_TOKEN
});
app.error(error => { console.error(error) });
app.use(async ({ client, payload, context, next }) => {
console.info('It\'s payload', JSON.stringify(payload))
if (!context?.retryNum) {
await next();
} else {
console.debug('app.use.context', context);
}
});
//**bot processing**//
// https://{your domain}.cloudfunctions.net/slack/events
module.exports = functions
.runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] })
.https.onRequest(expressReceiver.app);
But when I rewrote it for migration, I got the following error.
Is there a way to rewrite the code while avoiding this error?
Failed to load function definition from source: FirebaseError: Failed to load function definition from source: Failed to generate manifest from function source: Error: Apps used in a single workspace can be initialized with a token. Apps used in many workspaces should be initialized with oauth installer options or authorize.
Since you have not provided a token or authorize, you might be missing one or more required oauth installer options. See https://slack.dev/bolt-js/concepts#authenticating-oauth for these required fields.
I'm trying to create a Telegram bot but when I run the code I get these errors:
"node-telegram-bot-api deprecated Automatic enabling of cancellation of promises is deprecated
In the future, you will have to enable it yourself.
See https://github.com/yagop/node-telegram-bot-api/issues/319. internal\modules\cjs\loader.js:1063:30"
"error: [polling_error] {"code":"ETELEGRAM","message":"ETELEGRAM: 409 Conflict: terminated by
other getUpdates request; make sure that only one bot instance is running"}"
I have no idea what that means. I've followed the link but I still don't know how to solve the problem, please help me, I don't know what to do.
Here is my bot.js code if that helps:
const TelegramBot = require('node-telegram-bot-api');
const axios = require('axios');
const parser = require('./parser.js');
require('dotenv').config();
const token = process.env.TELEGRAM_TOKEN;
let bot;
if (process.env.NODE_ENV === 'production') {
bot = new TelegramBot(token);
bot.setWebHook(process.env.HEROKU_URL + bot.token);
} else {
bot = new TelegramBot(token, { polling: true });
}
// Matches "/word whatever"
bot.onText(/\/word (.+)/, (msg, match) => {
const chatId = msg.chat.id;
const word = match[1];
axios
.get(`${process.env.OXFORD_API_URL}/entries/en-gb/${word}`, {
params: {
fields: 'definitions',
strictMatch: 'false'
},
headers: {
app_id: process.env.OXFORD_APP_ID,
app_key: process.env.OXFORD_APP_KEY
}
})
.then(response => {
const parsedHtml = parser(response.data);
bot.sendMessage(chatId, parsedHtml, { parse_mode: 'HTML' });
})
.catch(error => {
const errorText = error.response.status === 404 ? `No definition found for the word: <b>${word}</b>` : `<b>An error occured, please try again later</b>`;
bot.sendMessage(chatId, errorText, { parse_mode:'HTML'})
});
});
Install this package npm I dotenv
Create a file called .env at the root of your project.
Add to that file this NTBA_FIX_319=1
Add to the top of your index.js file (or whatever file that contains your bot instance): require('dotenv').config()
Restart your bot.
So your code will look like
require('dotenv').config();
const TelegramBot = require('node-telegram-bot-api');
// replace the value below with the Telegram token you receive from #BotFather
const token = 'YOUR_TELEGRAM_BOT_TOKEN';
// Create a bot that uses 'polling' to fetch new updates
const bot = new TelegramBot(token, {polling: true});
bot.on('message', (msg) => {
const chatId = msg.chat.id;
// send a message to the chat acknowledging receipt of their message
bot.sendMessage(chatId, 'Received your message');
});
1) Get rid of the promise cancellation warning
The simplest, is to add that line at the top of your bot file:
process.env.NTBA_FIX_319 = 1 // this line SHOULD be above all imports
const TelegramBot = require('node-telegram-bot-api')
// ...rest of your telegram bot code
2) The 409 conflict error
In short, there are two version of your telegram bot running at the same time, so:
check if there is not multiple instance of your server running your bot, which can be the case when using workers, pm2...
check that your bot script is not started twice
Other causes and explanations can be found here and here
I'm trying to test an API with jest. Originally all my tests wherein one file and all tests where passing. I wanted to separate my tests into different files. To do this I'm trying to use a global setup file with beforeEach and afterEach. The problem is that sometimes I run the tests and they pass and other times they fail (different test pass and different fail each time).
package.json
{
"jest": {
"verbose": true,
"bail": false,
"preset": "#shelf/jest-mongodb",
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.js"
],
"globals": {
"authHeaders": {},
"authId": null
}
},
}
jest.setup.js
const app = require("./index")
const supertest = require("supertest")
const request = supertest(app)
const db = require("./src/db")
const jwtDecode = require("jwt-decode")
const getAuthToken = require("./src/utils/getAuthToken")
let token
let decodedJwt
const getAuthHeaders = token => ({
Authorization: `Bearer ${token}`
})
beforeEach(async done => {
//setup globals available in all tests
token = await getAuthToken()
token = JSON.parse(token)["access_token"]
decodedJwt = jwtDecode(token)
authId = decodedJwt.sub
authHeaders = getAuthHeaders(token)
// Ensure that we have a users collection with our test user
// who can then call the api
await request
.post("/v1/users")
.set(authHeaders)
.send({ email: "test#example.com" })
.expect(200)
done()
})
afterEach(async done => {
// Delete the users collection
await db.instance.dropUsers()
await db.instance.dropAgencies()
done()
})
users.test.js
const app = require("../../../../index")
const supertest = require("supertest")
const request = supertest(app)
const faker = require("faker")
describe("/v1/users/:id", () => {
test("should return an error when the users do not have a mutual agency", async done => {
if (!authHeaders || !authId) {
expect(true).toBe(false)
}
let testUserId = null
let email = faker.internet.email()
await request
.post("/v1/users")
.set(authHeaders)
.send({ email })
.expect(200)
.then(({ body }) => (testUserId = body._id))
await request
.get("/v1/users/" + testUserId)
.set(authHeaders)
.expect(200)
.then(({ body }) => {
expect(body).toHaveProperty("error")
expect(body.error).toHaveProperty("message")
})
done()
})
})
I can run the above once and it will pass and run again moments later and it will fail.
Edit:
After further investigation it seems that the issue is something to do with the afterEach function not dropping the Users collection. When I console log the response of the request made in the beforeEach in the jest.setup.js file I'm getting an error response saying that the user for the given email already exists.
The issue was caused by multiple tests running at the same time, adding the --runInBand flag (which forces test to be run serially) fixes the issue.
The error in my thinking was I assumed it was safe to add a user record to the database beforeEach and delete it afterEach without considering that if two tests run at the same time one would always fail as a user would already exist in the database.
In light of this I think it would be an idea to refactor my setup file so that my test user is only created once for all tests but I need to consider the implications of doing this.
EDIT:
I have found that I can run test successfully without the runInBand flag by switching out my atlas cloud database in favour of mongodb-memory-server during tests.
I'm learning FCM and I'm currently editing the index.js file to execute Firebase functions. However, when I deploy the function 'sendPushNotifications' I receive the error "Parsing error: Identifier 'functions' has already been declared." I've only declared it once within the file so I'm not sure if it's something beyond the file that I have to edit. I apologize for the poor formatting of the code below, I'm still not too used to pasting code into SO.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendPushNotifications = functions.https.onRequest((req,res) => {
response.send("Attempting to send push notification...")
console.log("LOGGER --- Trying to send push mesage")
var registrationToken = 'dSXeXBSHShU:APA91bFHWw_jNF1pr8Toq3OelqtyXrTZZssJW7YHMlP-tiNJ41uuO-pS--rfWduPFEEC72FchtDRHbt1RMM1e5kSWHUDVhWFvIAtx82LjIDiUNlmk14Ix_SLtrN_vB55rbr1tgcpS3CW';
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent message:', response);
return response
})
.catch((error) => {
console.log('Error sending message:', error);
throw new Error("Error sending message");
});
})
got the same error.
Check your code, you might have declared the "const function" multiple time ;)
I am attempting to create a Cognito user pool programmatically from a script using the JavaScript SDK.
I have successfully created the user-pool and defined a pre-signup and post-confirmation trigger by specifying the arn's of the relevant lambdas in my config. (as per the docs)
My script looks like this:
const aws = require('aws-sdk');
const awsConfig = require('../config/config');
aws.config.update({ region: awsConfig.REGION });
const provider = new aws.CognitoIdentityServiceProvider();
// user provided args
const stage = process.argv[2];
if (!stage) {
process.stdout.write('Please provide stage as argument\n');
process.exit(1);
}
// generate arns for pre and post cognito triggers
const getArn = (lambdaName) => {
return `arn:aws:lambda:${awsConfig.REGION}:${awsConfig.AWS_ACCOUNT_ID}` +
`:function:my-project-name-${stage}-${lambdaName}`;
};
const preSignUp = getArn('preSignUp');
const postConfirmation = getArn('postConfirmation');
const userPoolConfig = {
PoolName: `mypool-${stage}`,
AutoVerifiedAttributes: ['email'],
Schema: [
{
"StringAttributeConstraints": {
"MaxLength": "2048",
"MinLength": "0"
},
"Mutable": true,
"Required": true,
"AttributeDataType": "String",
"Name": "email",
"DeveloperOnlyAttribute": false
}
],
LambdaConfig: {
PostConfirmation: postConfirmation,
PreSignUp: preSignUp
}
};
const callback = (err, resp) => {
if (err) {
process.stdout.write(`${err}\n`);
} else {
process.stdout.write(resp.UserPool.Id);
}
};
provider.createUserPool(userPoolConfig, callback);
When I run this script it successfully creates the user pool, an and when I inspect it in the console the triggers are set correctly.
When I try to register a user on my user pool I get the error:
AccessDeniedException { code: 'UnexpectedLambdaException', ... }
If I go into the console and set the trigger manually it works just fine.
This bug has been reported - but I see no confirmation, nor solution:
https://github.com/aws/aws-cli/issues/2256
Desperately unable to fix or find a workaround.
If you need to add the permission in a serverless.yml file then this is what worked for us. Add it to your Resources section:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
Principal: cognito-idp.amazonaws.com
FunctionName: <function-name>
SourceArn: arn:aws:cognito-idp:<your region>:<your account>:userpool/*
This gives all the user pools the ability to invoke your particular function.
You can get the function name to use by looking in .serverless/serverless-state.json and there against your lambda you'll see the FunctionName property.
If you have set up your user pool and lambda triggers in cloud formation you will need to add the appropirate permissions for the user pool to invoke the lambda function.
You would have to add something like this to your cloud formation template.
"UserPoolPreSignupLambdaInvokePermission" : {
"Type" : "AWS::Lambda::Permission",
"Properties" : {
"Action" : "lambda:invokeFunction",
"Principal" : "cognito-idp.amazonaws.com",
"FunctionName" :{ "Ref" : "AutoVerifyEmailPreSignupLambdaFunction" },
"SourceArn" : {
"Fn::Sub" : "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPool}"
}
}
}
I managed to solve this problem. The issue is that the lambda does not have the correct permissions to interact with cognito.
I found this snippet of information hidden away here
So the in the callback function for the create user pool I attached the correct permissions like this:
const callback = (err, resp) => {
if (err) {
process.stdout.write(`${err}\n`);
} else {
const userPoolId = resp.UserPool.Id;
// the lambdas must have a permission attached that allows them to interact
// directly with cognito
const generateLambdaPersmission = (userPoolName, lambdaName) => {
return {
Action: 'lambda:InvokeFunction',
Principal: 'cognito-idp.amazonaws.com',
SourceArn: `arn:aws:cognito-idp:${awsConfig.REGION}:${awsConfig.AWS_ACCOUNT_ID}:userpool/${userPoolId}`,
FunctionName: getArn(lambdaName),
StatementId: `${stage}1`
};
};
lambda.addPermission(generateLambdaPersmission(userPoolId, 'preSignUp'), (err, resp) => {
if (err) {
process.stdout.write(`error attaching permission to lambda: ${err}`);
}
});
lambda.addPermission(generateLambdaPersmission(userPoolId, 'postConfirmation'), (err, resp) => {
if (err) {
process.stdout.write(`error attaching permission to lambda: ${err}`);
}
});
process.stdout.write(userPoolId);
}
};
See the documentation on adding permissions via the JavaScript SDK here