Node modules with sub-directories: "Error parsing triggers: Cannot find module 'ibm-watson'" - node.js

I have a Firebase Cloud Function that calls IBM Watson to get a token. I'm updating it from the old username/password auth to the current IAM auth.
Here's the code from the IBM documentation:
const watson = require('ibm-watson');
const { IamAuthenticator } = require('ibm-watson/auth');
// to get an IAM Access Token
const authorization = new watson.AuthorizationV1({
authenticator: new IamAuthenticator({ apikey: 'fakekey-1234' }),
});
authorization.getToken(function (err, token) {
if (!token) {
console.log('error: ', err);
} else {
// Use your token here
}
});
When I run firebase deploy --only functions I get this error:
Error: Error parsing triggers: Cannot find module 'ibm-watson'
Require stack:
- /Users/TDK/LanguageTwo/functions/index.js
- /Users/TDK/.nvm/versions/node/v13.10.1/lib/node_modules/firebase-tools/lib/triggerParser.js
ibm-watson is installed in my /functions/node_modules directory:
I reinstalled ibm-watson, and for good measure I ran npm install in my functions directory. Plus I ran npm-check and updated all my node modules.
The specific line that causes the error is:
const watson = require('ibm-watson');
When I comment out that line the functions deploy without error. Unfortunately, the function doesn't run. :-)
This line does not cause the deploy error:
const { IamAuthenticator } = require('ibm-watson/auth');
I use IBM Watson in other Firebase Cloud Functions in the same index.js file. These lines from other functions don't cause deploy errors:
let TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
...
var LanguageTranslatorV3 = require('ibm-watson/language-translator/v3');
The problem seems to be that requiring the parent directory ibm-watson fails, but requiring the subdirectories of the parent directory works. Any suggestions?

This is as expected. If you take a look at the GitHub repo for ibm-watson - https://github.com/watson-developer-cloud/node-sdk - you will notice that there is no example requiring the top level library. This stops you from pulling in the full library, when you only need to pull in a small sub-component.

Related

Firebase Cloud functions - Failed to load function definition from source: Restful Endpoints - Failed to add the endpoint defined

I am using firebase cloud functions with typescript in node 16.14.
My index.ts look like this:
import { FunctionParser } from "firebase-backend";
exports = new FunctionParser({rootPath: __dirname, exports,
verbose:true}).exports;
Here is the addEvent.endpoint.ts:
import { Request, Response } from "express";
import { Endpoint, RequestType } from "firebase-backend";
// import * as functions from "firebase-functions";
const fs = require('firebase-admin');
const serviceAccount = require('./key.json');
fs.initializeApp({
credential: fs.credential.cert(serviceAccount)
});
const db = fs.firestore();
export default new Endpoint(
"addEvent",
RequestType.POST,
(request: Request, response: Response) => {
const message = request.body["message"];
db.collection("events").doc("adkakjagjh").set("message", message);
return response.status(201).send({
message: message
});
}
)
I have the following file structure as below.
My cloud functions worked just recently but some change that I am not aware of has made them error out with the following error for just this 1 endpoint "addEvent.endpoint.ts": Error: Failed to load function definition from source: Failed to generate manifest from function source: Error: Restful Endpoints - Failed to add the endpoint defined in C:/Development/EventFeeder/Backend/Firebase/functions/lib/users/restful/addEvent.endpoint.js to the users Api.
How can I fix this issue?
I am not sure what I need to try because the error message is not that specific about the problem's root cause.
I did not find any post about this particular problem.
I try to run the functions with "npm run build && firebase emulators:start --only functions" and "firebase emulators:start"
The issue disappear when I remove the addEvent.endpoint.ts file
I was facing the same problem, and I figured out that you shouldn't initialize the app (initializeApp()) in each endpoint, just write it in the index.ts one time and everything will work as expected.

Firebase Functions: Upload Error: HTTP Error: 400, Unknown Error

I tried to deploy the server side of my Angular Universal SSR app to Firebase Functions, but ran into the error Upload Error: HTTP Error: 400, Unknown Error.
From what I understand, this error happens pretty often when the deployment is a huge file (in my case it's 438mb). The reason it's so big is because I'm deploying localized versions of my website so dist/browser and dist/server both have en, de, and fr directories with pretty much the same content. How can I solve this issue?
console output
=== Deploying to 'PROJECT_NAME'...
i deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint
+ functions: Finished running predeploy script.
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
+ functions: required API cloudfunctions.googleapis.com is enabled
i functions: preparing dist directory for uploading...
i functions: packaged dist (438.04 MB) for uploading
! functions: Upload Error: HTTP Error: 400, Unknown Error
Error: HTTP Error: 400, Unknown Error
index.js
const functions = require('firebase-functions');
// Increase readability in Cloud Logging
require("firebase-functions/lib/logger/compat");
const expressApp = require('./server/proxy').app();
exports.ssr = functions
.region('us-central1')
.runWith({})
.https
.onRequest(expressApp);
proxy.ts (which gets compiled to js and put into the dist/server folder)
import * as express from 'express';
import * as cookieParser from 'cookie-parser';
import { join } from 'path';
export function app() {
const server = express();
server.use(cookieParser());
const languages = ['en', 'de', 'fr'];
languages.forEach((locale) => {
const appServerModule = require(join(__dirname, locale, 'main.js'));
server.use(`/${locale}`, appServerModule.app(locale));
});
server.get('/(:locale(en|fr|de)/)?*', (req, res, next) => {
const { locale } = req.params;
let userLocale = (req.headers['accept-language'] || '').substring(0, 2);
if(!languages.includes(userLocale)) {
userLocale = 'en';
}
if (locale !== userLocale) {
res.redirect(userLocale + req.url);
}
});
return server;
}
function run() {
app().listen(4200, () => {
console.log(`Node Express server listening on http://localhost:4200`);
});
}
run();
I had the same problem with NextJS and firebase functions,
Here is my solution:
Remove node_modules and run npm i
Remove cache files, out or public builds
Check functions console registry from firebase panel (here you can check your serve status/logs)
I hope this could help to someone.
I decreased the size of my deployment, and it immediately fixed the issue. I would say you just have to optimize the files you have, or think about storing some files in another location other than the firebase functions codebase (perhaps firebase storage)
I had this issue and just was able to fix it. In my case I had deployed to firebase a number of times and with each deploy the upload directory got bigger and bigger. I ended up changing my hosting settings to only keep the last 3 deploys which ended up dropping my package size from around 170MB to 15MB once the previous deploys were deleted. For some reason with each deploy it would take in each previous deploy when trying to upload a new release which doesn't make sense to me.

Generating rss.xml for Angular 8 app locally works fine, but not on prod

I am trying to generate a rss.xml file for my angular 8 app with ssr + firebase + gcp inside the domain.
I've created a RssComponent which can be reached at /rss route. There i call getNews() method and receive an array of objects. Then I make a http request to /api/rss and in server.ts i handle that request:
app.post('/api/rss', (req, res) => {
const data = req.body.data;
const feedOptions = // defining options here
const feed = new RSS(feedOptions);
data.forEach((item) => {
feed.item({
title: item.headingUa,
description: item.data[0].dataUa,
url: item.rssLink,
guid: item.id,
date: item.utcDate,
enclosure: {url: item.mainImg.url.toString().replace('&', '&'), type: 'image/jpeg'}
});
});
const xml = feed.xml({indent: true});
fs.chmod('dist/browser/rss.xml', 0o600, () => {
fs.writeFile('dist/browser/rss.xml', xml, 'utf8', function() {
res.status(200).end();
});
});
});
And finally on response i'm opening the recently generated rss.xml file in RssComponent. Locally everything is working fine but on Google Cloud Platform it's not generating a file.
As explained in the Cloud Functions docs:
The only writeable part of the filesystem is the /tmp directory
Try changing the path to the file to the /tmp directory.
Nevertheless, using local files in a serverless environment is a really bad idea. You should assume the instance handling the following request will not be the same as the previous one.
The best way to handle this would be to avoid writing local files and instead storing the generated file in GCP Storage or Firebase Storage, and then retrieving it from there when needed.
This will ensure your functions are idempotent, and also will comply with the best practices.

Token passed to createTokenAuth is not a string : octokit, auth-token

I am trying to build a GitHub App and following the https://probot.github.io/docs/ and https://octokit.github.io/rest.js/v17#authentication. It is basically a nodejs app.
I have no experience working with nodejs or typescript and not even the probot framework.
The PRIVATE_KEY_PATH is in the .env file as follows:
PRIVATE_KEY_PATH=my-app.2020-04-03.private-key.pem
The .pem file is in the root directory of the project
The typeof prints string :
-------------TypeOf token ---- string
index.js
/**
* This is the main entrypoint to your Probot app
* #param {import('probot').Application} app
*/
const{Octokit} = require("#octokit/rest");
const{createAppAuth} = require("#octokit/auth-token");
console.log('PRIVATE_KEY',process.env.PRIVATE_KEY_PATH);
console.log('-------------TypeOf token ----', typeof process.env.PRIVATE_KEY_PATH)
const appOctokit = new Octokit({
authStrategy:createAppAuth,
auth:{
id:12345,
privateKey: process.env.PRIVATE_KEY_PATH,
// privateKey: 'token ${process.env.PRIVATE_KEY_PATH}'
}
});
I keep getting below error :
10:52:51.166Z ERROR probot: [#octokit/auth-token] Token passed to createTokenAuth is not a string
Not able to find much help on this topic over teh internet. I even tried navigating the code of octokit https://github.com/octokit/auth-token.js/blob/master/src/index.ts and it seems I am doing nothing wrong in my code.
There are not much resources to refer for issues for GitHub Apps or probot framework apart from the documentations. StackOverflow too ha just about 20-30 questions related to GitHub Apps or probot framework.
EDIT 1 : START
Running below code :
/**
* This is the main entrypoint to your Probot app
* #param {import('probot').Application} app
*/
const{Octokit} = require("#octokit/rest");
const{createAppAuth} = require("#octokit/auth-token");
console.log('PRIVATE_KEY',process.env.PRIVATE_KEY_PATH);
console.log('-------------TypeOf token ----', typeof process.env.PRIVATE_KEY_PATH)
produces a below error:
ERROR probot: appFn is not a function
TypeError: appFn is not a function
EDIT 1 : END
If you use Probot, you don't need to load your own #octokit/rest or any of the #octokit/auth-* packages, it's all built into Probot
Did you try the example code shown on https://probot.github.io/
module.exports = app => {
app.on('issues.opened', async context => {
const params = context.issue({
body: 'Hello World!'
})
await context.github.issues.createComment(params)
})
}
Probot will automatically read the contents of your .env file, look for the PRIVATE_KEY_PATH environment variable, read out the contents of the file at that location, and setup the JWT/installation authentication for you

Can't determine Firebase Database URL when trying to read Firebase Database from within Node.js Firebase function

I am using Flutter to build an app that accesses the Firebase database. All good from the Flutter side....but I am new to Node.js and Cloud Functions. I am trying to create a Node.js function to react to a deletion event of a record on one Firebase Database node and then delete records from two other Firebase Database nodes and image files from Firestore.
I am reacting to the trigger event with a functions.database.onDelete call, no problem, but falling at the very next hurdle i.e.trying to read admin.database to get a snapshot.
I have created a dummy function that uses .onUpdate to pick up a trigger event (don't want to keep having to recreate my data as I would if I used .onDelete) and then tries to read my Firebase Database to access a different node. The trigger event is picked up fine but I don't seem to have a database reference Url to do the read...yet it is the same database. Output on the console log from a call to process.env.FIREBASE_CONFIG shows the Url is present.
The included function code also has commenting to show the various outputs I get on the console log.
I am going crazy over this.....PLEASE can anyone tell me where I am going wrong. Been searching Google, Stackoverflow, Firebase docs for the last two days :-(
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp({
credential: admin.credential.cert(
require("./user-guy-firebase-adminsdk.json")
)
});
exports.testDeleteFunction = functions.database
.ref("/user-guys/{userGuyId}")
// Using .onUpdate as I don't want to keep restoring my data if I use .onDelete
.onUpdate((snapshot, context) => {
const userData = snapshot.after.val();
const userId = userData.userId;
console.log('userId: ' + userId); // Gives correct console log output: userId: wajzviEcUvPZEcMUbbkPzcw64MB2
console.log(process.env.FIREBASE_CONFIG);
// Gives correct console log output:
// {"projectId":"user-guy","databaseURL":"https://user-guy.firebaseio.com","storageBucket":"user-guy.appspot.com","cloudResourceLocation":"us-central"}
// I have tried each of the four following calls and received the console log message as indicated after each.
//
// var root = admin.database.ref(); // Console Log Message: TypeError: admin.database.ref is not a function
// var root = admin.database().ref(); // Console Log Message: Error: Can't determine Firebase Database URL.
// var root = admin.database.ref; // Fails at line 31 below with the message indicated.
// var root = admin.database().ref; // Console Log Message: Error: Can't determine Firebase Database URL.
console.log(root.toString); // Console Log Message: TypeError: Cannot read property 'toString' of undefined.
// This is intended to read a chat thread for two users but processing never gets here.
var database = root.child('chats').child('0cSLt3Sa0FS26QIvOLbin6MFsL43GUPYmmAg9UUlRLnW97jpMCAkEHE3');
database
.once("value")
.then(snapshot => {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null; // Will do stuff here once working.
});
Error messages shown in code comments.
If you want to use the configuration in FIREBASE_CONFIG, you should initialize the Admin SDK with no parameters:
admin.initializeApp();
This will use the default service account for your project, which should have full read and write access to the database.
You need to add your database url in admin.initializeApp
admin.initializeApp({
databaseURL:"your_database_url"
});
select Realtime Database in firebase and copy your url add in settings in fire config or watch this video https://www.youtube.com/watch?v=oOm_9y3vb80
config example
const config = {
apiKey: "",
authDomain: "",
projectId: "",
databaseURL: "https://youUrl.firebaseio.com/",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
See:
https://firebase.google.com/docs/admin/setup#initialize_without_parameters
https://firebase.google.com/docs/functions/database-events
Try initialize without parameters.
The SDK can also be initialized with no parameters. In this case, the SDK uses Google Application Default Credentials and reads options from the FIREBASE_CONFIG environment variable. If the content of the FIREBASE_CONFIG variable begins with a { it will be parsed as a JSON object. Otherwise the SDK assumes that the string is the name of a JSON file containing the options.
const admin = require("firebase-admin"); // Import Admin SDK
const functions = require("firebase-functions"); // Import Cloud Functions
admin.initializeApp();
Same issue 3 years later...
After DAYS! I found out all of the Flutter documents have code for Firestore instead of Firebase.
Basically there are two products. Firestore Database, and Real-time Database. You are calling the Real-time Database methods, but probably have a Firestore database.
Try admin.firebase().ref('/some_collection').push();
Basically everywhere you're calling .database(), replace it with .firebase() if you are using Firebase. Yes, the Flutter tutorials are mis-leading!!!

Resources