Leveraging Typescript in Firebase Cloud Functions to effectively work with intricate interfaces

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;

Answer №1

The problem has been successfully resolved. It appears that the issue stemmed from how I imported Timestamp.

const app_1 = require("firebase/app");
var Timestamp = app_1.firestore.Timestamp;

The correct way to import is:

const app_1 = require("firebase");
var Timestamp = app_1.firestore.Timestamp;

IMPORTANT Note:

  • It seems that when using firebase deploy --only functions, it may utilize a cached version of your function if it cannot resolve the current one. This became evident to me when running lint did not show any errors even though there was a reference to Timestamp in my function. It seemed like the deployment went smoothly because it used a cached version of the same function already deployed.

  • I only discovered the issue when I switched computers and had to reinstall firebase-cli, redeploy, and then it highlighted the incorrect reference to Timestamp.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error: The object 'exports' is not defined in geotiff.js at line 3

Looking to integrate the geotiff library with Angular 6.1.0 and TypeScript 2.9.2. Installed it using npm i geotiff Encountering the following error in the browser console: Uncaught ReferenceError: exports is not defined at geotiff.js:3 After r ...

Using an existing function with no arguments as a handler in Typescript and React: A Step-by-Step Guide

NOTE: I'm still learning Typescript, so I may be missing something obvious here. Let's consider a basic scenario in React Javascript, using a Material-UI Button: // Closing dialog event handler without needing an 'event' argument const ...

Retrieve the most recently added child from the Firebase cloud function

Seeking assistance in retrieving the most recently added child in a cloud function. Below is the code snippet I am using and I'm curious if there is a specific function or query I can utilize to achieve this task without having to iterate through each ...

The discord.js TypeScript is throwing an error stating that the 'index.ts' file is missing when trying to run 'ts-node index.ts'

I have been working on creating a discord bot using discord.js and TypeScript. However, when I attempt to start the bot by running 'ts-node index.ts', I encounter the following error: Error: Cannot find module 'node:events' Require stac ...

When the button is clicked, a fresh row will be added to the table and filled with data

In my table, I display the Article Number and Description of werbedata. After populating all the data in the table, I want to add a new article and description. When I click on 'add', that row should remain unchanged with blank fields added below ...

Are there more efficient alternatives to utilizing arrays and index-based functions for storing in-memory data in TypeScript?

Is there a more efficient method for storing and retrieving data besides relying on Array index-based calls? For instance: export interface EntityInterface { id: number; name: string; age: number; } export class ClassName { entities: Enti ...

The Angular application has encountered a stack overflow due to exceeding the maximum

./src/main.ts - An issue occurred: The module build process failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js): Error: Maximum call stack size exceeded import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; { App ...

DateAdapter not found within Angular/Material/Datepicker - Provider not available

I need assistance with: angular / material / datepicker. My test project is running smoothly and consists of the following files: /src/app/app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from ' ...

Using the TranslateService in Angular to externalize an array of strings

I am new to externalizing code. As I was working on developing a month picker in Angular, I initially had an array of months with hardcoded names in my typescript file: arr = ['Jan', 'Feb', 'Mar', 'Apr', 'May&a ...

What is the reason for the lack of variable assignment within the forEach method in Angular?

I am struggling with assigning a value to the variable "this.qdDias" and returning it. After using subscribe, I am unable to retrieve the value at the end of the method. Despite seeing the value in the console.log(this.qdDias), it becomes undefined when re ...

Is there a way to ensure an ajax call finishes executing without relying on 'async: false' or callbacks?

In my view, I have implemented a TypeScript code defining a KnockoutJS binding handler for a clickable element as shown below: module MyModule { export interface ICopyButtonParams { dataUrl: string; } ko.bindingHandlers.copyButton = { ...

What steps should I take to successfully compile my Typescript Webpack project without any errors?

Currently, I am attempting to convert only two .js files into .ts files within my webpack node.js project and then compile them (actions.ts and flux.ts). When I execute the command: webpack --progress --colors I encounter the following errors: 'use ...

Click to Rotate Angular Chevron

Is it possible to animate the rotation of a chevron icon from left-facing to right-facing using Angular? CSS: .rotate-chevron { transition: .1s linear; } HTML: <button [class.button-open]="!slideOpen" [class.button-close]="slideOpe ...

Creating a type-safe dictionary for custom theme styles in Base Web

In my Next.js project, I decided to use the Base Web UI component framework. To customize the colors, I extended the Theme object following the guidelines provided at . Interestingly, the documentation refers to the theme type as ThemeT, but in practice, i ...

Angular 2's abstract component functionality

What are the benefits of utilizing abstract components in Angular 2? For example, consider the following code snippet: export abstract class TabComponent implements OnInit, OnDestroy {...} ...

Issues may arise in Typescript when trying to return an array of data from a redux createAsyncThunk function

Below is the code I am using to retrieve a list of users: export const fetchUserById = createAsyncThunk( "users/fetchById", async (_, { rejectWithValue, fulfillWithValue }) => { try { const response = await fetch(`https://reqres. ...

Modifying iframe src using click event from a separate component in Angular 10

I am looking to dynamically update the src attribute of an iframe when the menu bar is clicked. The menu bar resides in a separate component and includes a dropdown menu for changing languages. Depending on which language is selected, I want to update the ...

Is there a way to incorporate a dropdown feature into a search bar using Ant Design?

I'm currently working on a project that requires me to incorporate two drop-down menus inside the search box. Despite following the guidelines provided in the documentation (https://ant.design/components/input), I encountered a problem when trying to ...

Scanning for devices on Ionic 2/3 made simple: How to easily exclude unwanted application and Android directories

I'm currently working on a gallery application that enables users to choose images from their phone and transfer them to a kiosk. Upon loading the application, it searches the entire device for folders containing images and organizes them into an albu ...

Is it possible to import node_modules from a specific directory mentioned in the "main" section of the package.json file?

Is it feasible to import from a source other than what is defined by the "main" setting? In my node_modules-installed library, the main file is located at lib/index.js With es2015 imports (source generated from ts compiled js), I can use the following ...