When querying a collection on the app side, I am able to automatically cast the result as an interface using Positions constructor that takes in interface IPosition.
However, attempting to do the same on the cloud functions side prevents the functions from deploying. Debugging the code becomes challenging because it needs to be deployed and only works when the code is functioning correctly (local serve requires certain permissions).
After removing most of the code and re-adding it line by line, I discovered that the issue lies with the interface having properties of type enum
. Casting position
as IPosition also does not work. In addition, the interface is imported from another module (parent app module).
import { Position } from '../../src/app/models/position';
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { UserRecord } from 'firebase-functions/lib/providers/auth';
admin.initializeApp();
const promisePool = require('es6-promise-pool');
const PromisePool = promisePool.PromisePool;
// const secureCompare = require('secure-compare');
const MAX_CONCURRENT = 3;
const store = admin.firestore();
exports.updateMetrics = functions.https.onRequest((req, res) => {
// const key = req.query.key;
// // Exit if the keys don't match.
// if (!secureCompare(key, functions.config().cron.key)) {
// console.log(
// 'The key provided in the request does not match the key set in the environment. Check that',
// key,
// 'matches the cron.key attribute in `firebase env:get`'
// );
// res
// .status(403)
// .send(
// 'Security key does not match. Make sure your "key" URL query parameter matches the ' +
// 'cron.key environment variable.'
// );
// return null;
// }
// Fetch all user.
return getUsers()
.then(users => {
// Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.
const pool = new PromisePool(
() => runMetricsAnalysis(users),
MAX_CONCURRENT
);
return pool.start();
})
.then(() => {
console.log('metrics updated');
res.send('metrics updated');
return null;
});
});
/**
* Returns the list of all users.
*/
function getUsers(users: UserRecord[] = [], nextPageToken?: string) {
let tempUsers: UserRecord[] = users;
return admin
.auth()
.listUsers(1000, nextPageToken)
.then(result => {
// Concat with list of previously found users if there was more than 1000 users.
tempUsers = tempUsers.concat(result.users);
// If there are more users to fetch we fetch them.
if (result.pageToken) {
return getUsers(tempUsers, result.pageToken);
}
return tempUsers;
});
}
function runMetricsAnalysis(users: UserRecord[]) {
if (users.length > 0) {
const user = users.pop();
if (user != null) {
return getPositions(user)
.then(positions => {
const metrics = generateMetrics(positions);
console.log('metrics', metrics);
return null;
// return writeMetrics(user.uid, metrics).catch(function(err) {
// console.error(err);
// return null;
// });
})
.catch(function(err) {
console.error(err);
return null;
});
}
return null;
}
return null;
}
/**
* Returns the list of positions for the previous month.
*/
function getPositions(user: UserRecord) {
return store
.collection(`users/${user.uid}/positions`)
.orderBy('postedDate', 'desc')
.get()
.then(querySnapshot => querySnapshot.docs.map(doc => doc.data()));
}
interface IMetrics {
portfolioValue: number;
profitLoss: number;
fees: number;
}
/**
* Generate metrics from positions
*/
function generateMetrics(positions: Array<any>): IMetrics {
let portfolioValue = 0;
let profitLoss = 0;
let fees = 0;
if (positions.length > 0) {
console.log('positions 5', positions);
positions
.map(position => new Position(position))
.map(position => {
portfolioValue += position.positionValue;
profitLoss += position.profitLossClosedQuantity;
fees += position.fees;
});
}
const IMetric = {
portfolioValue: portfolioValue,
profitLoss: profitLoss,
fees: fees
};
return IMetric;
}
Position
export interface IPosition {
...
}
export class Position implements IPosition {
...
constructor(position: IPosition) {
...
}
}
Update:
Previously, I did not notice any errors which could have been due to deploying a cached version of the function that worked. However, upon further investigation, I encountered the following error:
Error: Error occurred while parsing your function triggers.
TypeError: Cannot read property 'Timestamp' of undefined
at Object.<anonymous> (/Users/AceGreen/Library/Mobile Documents/com~apple~CloudDocs/Dev/Web/TradingTracker/functions/lib/src/app/models/position.js:5:33)
at Module._compile (internal/modules/cjs/loader.js:736:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:747:10)
at Module.load (internal/modules/cjs/loader.js:628:32)
at tryModuleLoad (internal/modules/cjs/loader.js:568:12)
at Function.Module._load (internal/modules/cjs/loader.js:560:3)
at Module.require (internal/modules/cjs/loader.js:665:17)
at require (internal/modules/cjs/helpers.js:20:18)
at Object.<anonymous> (/Users/AceGreen/Library/Mobile Documents/com~apple~CloudDocs/Dev/Web/TradingTracker/functions/lib/index.js:3:20)
at Module._compile (internal/modules/cjs/loader.js:736:30)
position.js translation
const app_1 = require("firebase/app");
var Timestamp = app_1.firestore.Timestamp;