Change region of firebase cloud functions of callable in v2 - node.js

Hey so I am trying to upgrade to v2 of firebase cloud functions, but when trying to change the code I noticed that my functions do not have .region anymore like in v1.
Here the v1 version where I could call .region and change it
import * as functions from "firebase-functions";
exports.helloWorld = functions.region("europe-west1").https.onCall(() => {
functions.logger.info("Hello logs!", { structuredData: true });
return { text: "Hello from Firebase!" };
});
now I upgraded to v2, but I get:
Property 'region' does not exist on type
'typeof import("/.../node_modules/firebase-functions/lib/v2/index")
Trying to achieve something like this for v2 of firebase cloud functions any ideas ?
import { https, logger } from "firebase-functions/v2";
import * as functions from "firebase-functions/v2";
// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
//
const regionalFunctions = functions.region("europe-west1");
exports.helloWorld = regionalFunctions.https.onCall(() => {
logger.info("Hello logs!", { structuredData: true });
return { text: "Hello ${process.env.PLANET} and ${process.env.AUDIENCE}" };
});

You can specify the region in the function's options as shown below:
import { onCall } from "firebase-functions/v2/https";
export const testFunction = onCall({ region: "..." }, (event) => {
// ...
})

Related

How to mock #google-cloud/kms using jest

I'm trying to write unit test cases for decrypt. I've my own implementation of decrypting an encrypted file. While trying to import the decrypt.mjs facing the following error.
Must use import to load ES Module: /node_modules/bignumber.js/bignumber.mjs
My application is a react frontend and NodeJS backend. I've used ES6 modules for NodeJS. Here is my decrypt.mjs file
import { readFile } from 'fs/promises';
import path from 'path';
import { KeyManagementServiceClient } from '#google-cloud/kms';
const decrypt = async (APP_MODE, __dirname) => {
if (APP_MODE === 'LOCALHOST') {
const keys = await readFile(
new URL(`./stagingfile.json`, import.meta.url)
).then((data) => JSON.parse(data));
return keys;
}
const { projectId, locationId, keyRingId, cryptoKeyId, fileName } =
getKMSDefaults(APP_MODE);
const ciphertext = await readFile(
path.join(__dirname, `/${fileName}`)
);
const formattedName = client.cryptoKeyPath(
projectId,
locationId,
keyRingId,
cryptoKeyId
);
const request = {
name: formattedName,
ciphertext,
};
const client = new KeyManagementServiceClient();
const [result] = await client.decrypt(request);
return JSON.parse(result.plaintext.toString('utf8'));
};
const getKMSDefaults = (APP_MODE) => {
//Based on APP_MODE the following object contains different values
return {
projectId: PROJECT_ID,
locationId: LOCATION_ID,
keyRingId: KEY_RING_ID,
cryptoKeyId: CRYPTO_KEY_ID,
fileName: FILE_NAME,
};
};
export default decrypt;
I tried to mock the #google-cloud/kms using manual mock (jest) but it didn't work. I tried multiple solutions to mock but nothing worked and it ended with the Must use import to load ES Module error.
I've had successfully used jest to mock #google-cloud/kms with TypeScript, so hopefully this will be the same process for ES modules that you can use.
Example working code:
// jest will "hoist" jest.mock to top of the file on its own anyway
jest.mock("#google-cloud/kms", () => {
return {
KeyManagementServiceClient: jest.fn().mockImplementation(() => {
return {
encrypt: kmsEncryptMock,
decrypt: kmsDecryptMock,
cryptoKeyPath: () => kmsKeyPath,
};
}),
};
});
// give names to mocked functions for easier access in tests
const kmsEncryptMock = jest.fn();
const kmsDecryptMock = jest.fn();
const kmsKeyPath = `project/location/keyring/keyname`;
// import of SUT must be after the variables used in jest.mock() are defined, not before.
import { encrypt } from "../../src/crypto/google-kms";
describe("Google KMS encryption service wrapper", () => {
const plaintext = "some text to encrypt";
const plaintextCrc32 = 1897295827;
it("sends the correct request to kms service and raise error on empty response", async () => {
// encrypt function is async that throws a "new Error(...)"
await expect(encrypt(plaintext)).rejects.toMatchObject({
message: "Encrypt: no response from KMS",
});
expect(kmsEncryptMock).toHaveBeenNthCalledWith(1, {
name: kmsKeyPath,
plaintext: Buffer.from(plaintext),
plaintextCrc32c: { value: plaintextCrc32 },
});
});
});

React and Easybase - Invalid hook call. Hooks can only be called inside of the body of a function component

I am trying to use React and Easybase (database). I'm having some issues however.
This is in the SolanaSignature.tsx file.
import { useWallet } from '#solana/wallet-adapter-react';
import bs58 from 'bs58';
import React, { FC, useCallback } from 'react';
import ReactDOM from 'react-dom';
import { sign } from 'tweetnacl';
import AddUser from './mainstorage';
export const SignMessageButton : FC = () => {
const { publicKey, signMessage } = useWallet();
const onClick = useCallback(async () => {
try {
if (!publicKey) throw new Error('Wallet not connected!');
if (!signMessage) throw new Error('Wallet does not support message signing! Please use a wallet such as Phantom or Solflare! NOTE: Some Ledgers wallets are not supported!');
const message = new TextEncoder().encode('Omega Protocol - Signature verification for Bold Badgers.');
const signature = await signMessage(message);
if (!sign.detached.verify(message, signature, publicKey.toBytes())) throw new Error('Invalid signature!');
//alert(`Message signature: ${bs58.encode(signature)}`);
AddUser();
} catch (error: any) {
alert(`Signing failed: ${error?.message}`);
}
}, [publicKey, signMessage]);
return signMessage ? (<button className="wallet-adapter-button wallet-adapter-button-trigger shine" onClick={onClick} disabled={!publicKey}>Verify</button>) : null;
};
and then the mainstorage file:
import { useEffect } from 'react';
import { useEasybase } from 'easybase-react';
const AddUser = () => {
const { db } = useEasybase();
useEffect(() => {
db('OMEGABB').insert({ walletid: "test", discordid: "test", signature: "test", valid: false, lastvalid: new Date() }).one()
.then(() => console.log("Success!"));
}, [])
return (
{/* ... */}
);
}
export default AddUser;
What is happening however when I click the button is that it comes up with a warning: Hooks can only be called inside the body of a function component.
This does work in the initial index file (aka the parent file) but does not work here. Right now this is only a dummy/test but trying to get it writing to the database.
Thanks!
As per React's documentation:
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns.
Currently, you're attempting to call a hook inside the onClick handler - AddUser is a custom hook since it also uses hooks and the better name for it should be useAddUser.
I suggest to make some improvements by returning a function from your custom hook that you can call to add a new user, e.g.:
export const useAddUser = () => {
const {db} = useEasybase()
const addUser = React.useCallback(() => {
db('OMEGABB')
.insert(/*...*/)
.then(/*...*/)
.catch(/*...*/)
}, [db])
return {
addUser,
/*...*/
}
}
Then, you can use useAddUser in the following way:
const {useAddUser} from './mainstorage'
const SignMessageButton: FC = () => {
const {publicKey, signMessage} = useWallet()
const {addUser} = useAddUser();
const onClick = React.useCallback(
async () => {
try {
// ...
addUser()
} catch (error) {/*...*/}
},
[publicKey, signMessage, addUser]
)
/*...*/
}

How to inject dependencies into Firebase/Google Cloud Functions? (unit & integration testing)

I don't know whether my question is really related to Firebase Cloud Functions, but I came across this problem trying to test my Firebase Cloud Functions.
Let's say I have a Firebase Cloud function written in NodeJS:
function.ts
import * as functions from "firebase-functions"
const admin = require("firebase-admin")
import * as authVerifier from "../../auth/authVerifier"
export default functions.https.onRequest(async (req, res) => {
let authId
try {
authId = await authVerifier.identifyClientRequest(req)
} catch (err) {
console.error(`Unauthorized request error: ${err}`)
return res.status(401).send({
error: "Unauthorized request"
})
}
}
Usually I have an interface and can easily mock any class I want to test it.
And, for example, authVerifier looks like:
authVerifier.ts
import * as express from "express"
export async function identifyClientRequest(req: express.Request) {
return true // whatever, it doesn't really matter, should be real logic here
}
I'm trying to test function.ts and I only can pass res and req into it, e.g:
function.test.ts
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = { }
await myFunctions(req as express.Request, res as express.Response)
// assert that authVerifier.identifyClientRequest(req) called with passed req
})
So the question is: how can I mock authVerifier.identifyClientRequest(req) to use different implementations in function.ts and in function.test.ts?
I don't really know NodeJS/TypeScript, so I wonder if I can import another mock class of authVerifier for test or something like that.
Ok, seems like I found the answer. I'll post it here just in case.
Using sinonjs, chai we can stub our class (authVerifier in that case) to return necessary results:
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
import * as authVerifier from "../../../src/auth/authVerifier"
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = mocks.createMockResponse()
const identifyClientRequestStub = sinon.stub(authVerifier, "identifyClientRequest");
const authVerifierStub = identifyClientRequestStub.resolves("UID")
await updateUser(req as express.Request, res as express.Response)
assert.isTrue(authVerifierStub.calledWith(req))
})
And the result is:

Does aws-sdk-mock support mocking of AWS SSM (Parameter Store)?

I am trying to mock AWS SSM using aws-sdk-mock with the code below but not working. Does not throw error, fetch the values from Actual store when getParametersByPath is called.
I had a look at the aws-sdk-mock documentation but does not seem to have an example for mocking ssm, is it supported or not.
AWSMock.mock('SSM', 'getParametersByPath', (params, callback) => {
callback(null, mockResponse);
});
I ran across this when trying to do a similar operation: When trying to mock SSM functionality the resources were still attempting to make requests to AWS and were not using the mock functionality.
Example:
import { mock } from 'aws-sdk-mock';
import { SSM } from 'aws-sdk';
import { GetParameterRequest, GetParameterResult } from 'aws-sdk/clients/ssm';
import 'mocha'
...
const ssm: SSM = new SSM();
mock('SSM', 'getParameter', async (request: GetParameterRequest) => {
return { Parameter: { Value: 'value' } } as GetParameterResult;
})
const request: GetParameterRequest = { Name: 'parameter', WithDecryption: true};
const result: GetParameterResult = await ssm.getParameter(request).promise();
expect(result.Parameter.Value).to.equal('value');
...
The error occurred when making the call to getParameter.
Turns out that the reason for our error was that we were instantiating the integration prior to declaring our mock. So the fix was to switch the order of execution and declare the mock before instantiating the integration.
Example:
import { mock } from 'aws-sdk-mock';
import { SSM } from 'aws-sdk';
import { GetParameterRequest, GetParameterResult } from 'aws-sdk/clients/ssm';
import 'mocha'
...
mock('SSM', 'getParameter', async (request: GetParameterRequest) => {
return { Parameter: { Value: 'value' } } as GetParameterResult;
});
// -> Note the following line was moved below the mock declaration.
const ssm: SSM = new SSM();
const request: GetParameterRequest = { Name: 'parameter', WithDecryption: true};
const result: GetParameterResult = await ssm.getParameter(request).promise();
expect(result.Parameter.Value).to.equal('value');
...

sinon.spy in my Node.JS project when testing an AWS service not working as expected

in my Node.JS project (a backend for an Angular 5 project) I have created a service that deals with the AWS Authentication... I have called this awsAuthenticationService. All works well but I now need to test it. In my awsAuthenticationService.js I have the following method that has some minor logic and then calls a method provided by the "cognitoIdentityServiceProvider". Here is a snippet of my code (I really have reduced this)
constructor() {
this._cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider(this.cognitoConfig);
}
toggleUserAccess(userName, type) {
const params = {
Username: userName,
UserPoolId: this.cognitoConfig.userPoolId
};
if (type === null) {
return this._cognitoIdentityServiceProvider.adminEnableUser(params).promise();
}
return this._cognitoIdentityServiceProvider.adminDisableUser(params).promise();
}
As you can see from the toggleUserAccess we pass a few parameters, determine what they are then call the appropriate method. I wish to test this by having a unit test that will call the authenticationService.toggleUserAccess, pass some params and spy on the authenticationService._cognitoIdentityServiceProvider methods to see if they were called. I set it up so...
let authenticationService = require('./awsAuthenticationService');
describe('toggleUserAccess', () => {
beforeEach(() => {
authenticationService._cognitoIdentityServiceProvider = {
adminDisableUser(params) {
return {
promise() {
return Promise.resolve(params);
}
};
}
};
authenticationService._cognitoIdentityServiceProvider = {
adminEnableUser(params) {
return {
promise() {
return Promise.resolve(params);
}
};
}
};
});
it('should call adminEnableUser if the type is null', () => {
authenticationService.toggleUserAccess('TheUser', null);
const spyCognito = sinon.spy(authenticationService._cognitoIdentityServiceProvider, 'adminEnableUser');
expect(spyCognito.calledOnce).to.equal(true);
});
it('should call adminDisableUser if the type is null', () => {
authenticationService.toggleUserAccess('TheUser', '0001');
const spyCognito = sinon.spy(authenticationService._cognitoIdentityServiceProvider, 'adminDisableUser');
expect(spyCognito.calledOnce).to.equal(true);
});
});
My tests aren't passing and I think I have set up my sinon.spys incorrectly - can anyone see what I am doing wrong or give advice please
To stub class of AWS.CognitoIdentityServiceProvider, need to stub with its prototype keyword.
// add require statement for your AWS class
const spyCognito = sinon.spy(AWS.CognitoIdentityServiceProvider.prototype, 'adminDisableUser');
expect(spyCognito.calledOnce).to.equal(true);
Hope it helps

Resources