How to call Firebase cloud function from React stream chat app - reactjs

Long story short, I'm knew to Firebase and I need to call this cloud function: "ext-auth-chat-getStreamUserToken" . The trigger is a HTTPS request to: https://europe-west2-FIREBASE-USER-INFO/ext-auth-chat-getStreamUserToken
Context: I've built a messaging app using Stream.io, with hard-coded user data and a placeholder token. Here's the key bit of code:
useEffect(() => {
async function init() {
const chatClient = StreamChat.getInstance(apiKey)
await chatClient.connectUser(user, chatClient.devToken(user.id))
const channel = chatClient.channel("messaging", "war-chat", {
image:
"https://i.picsum.photos/id/1006/200/200.jpg?hmac=yv53p45TOMz8bY4ZXUVRMFMO0_6d5vGuoWtE2hJhxlc",
name: "Oli",
members: [user.id],
//chat description
})
await channel.watch()
setClient(chatClient)
}
init()
if (client) return () => client.disconnectUser()
}, [])
I have installed the 'authenticate with stream chat' extension on Firebase. This creates various cloud functions, include one which creates a new user on Stream when a new user is created on firebase.
The only thing I need to do is modify this bit of code, to integrate firebase with stream chat:
await chatClient.connectUser(user, chatClient.devToken(user.id))
I can sort the user object. but how do I call the cloud function to get the token?

Per the source code of the getStreamUserToken Cloud Function (linked on the extension's product page), it is a Callable Cloud Function.
export const getStreamUserToken = functions.handler.https.onCall((data, context) => { /* ... */ });
Calling the Cloud Function can be done using the Firebase Client SDKs as documented with examples here in the docs. Because your cloud function resides in the europe-west2 region (and not the default us-central1 region), you will need to specify it when getting an instance of the Functions builder class:
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions(undefined, "europe-west2") // undefined is passed in so that the default app is used
const getStreamUserToken = functions.callable("ext-auth-chat-getStreamUserToken");
const streamUserToken = await getStreamUserToken();
The revoke function is called in the same way.
const revokeStreamUserToken = functions.callable("ext-auth-chat-revokeStreamUserToken");
await revokeStreamUserToken();

Related

How can I create a React front end that allows me to deploy a Solidity contract?

I am able to build the interface for the React app. I am able to create a deploy script using "#nomiclabs/hardhat-ethers" that compiles a .sol contract and successfully deploys it. What I cannot do is combine the two.
What I want is to click a button in the React app to deploy the contract.
I want to use hardhat's ethers.getContractFactory() method to compile and then deploy.
When I add "#nomiclabs/hardhat-ethers" to my project with
const { ethers } = require("#nomiclabs/hardhat-ethers");
I get an error about Hardhat context being required, so i add
require('hardhat').config()
Then I get
"Module not found: can't resolve 'async_hooks' in /node_modules/undici/lib/api"
Is there another library I can use to achieve what I want, or am I just missing something/doing something wrong?
require('dotenv').config();
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(process.env.REACT_APP_ALCHEMY_KEY);
require('hardhat').config();
const { ethers } = require("#nomiclabs/hardhat-ethers");
export const deployAuction = async (options) => {
const AuctionFactory = new ethers.getContractFactory("Auction");
// Start deployment, returning a promise that resolves to a contract object
const auction = await AuctionFactory.deploy(
options.token,
options.paymentToken,
options.bidIncrement,
options.timeoutPeriod
);
console.log("Contract deployed to address:", auction.address);
return auction.address;
};
Please, any help is appreciated. Thank you!
Once you deployed the contract with hardhat, you will see artifacts folder which stores the deployed contracts. since your contract name is Auction you will have artifacts/contracts/Auction/Auction.json. this JSON files includes the abi code of your contract. to create a contract instance you need to bring it to your frond-end.
To create a contract instance you need 3 arguments:
1- Address of the contract
2- abi of the contract
3- provider
// I wrapped with function to be able to use "await"
const createContract = async () => {
// make sure you pass correct path
// fetching json works in next.js, I believe React18 supprts it
const res = await fetch('artifacts/contracts/Auction/Auction.json');
const artifact = await res.json();
// address is inisde the json file. 5 is goeerli.
// depending on your config, you might have a different networkId
const address = artifact.networks[5].address;
// in react.js window.ethereum will be defined. in next.js you need to add if(window.ethereum){code here}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const contract = new ethers.Contract(address, artifact.abi, provider);
return contract;
};

How can I access a library through a script in a Typescript React app?

I am fairly new to React, and have not done any extensive web development in years, so am struggling with a (probably) basic web issue:
I am implementing a Stripe based payment flow in a React web app (written in Typescript), and have hit a roadblock on step 2 (adding a redirect to checkout client-side).
The quickstart guide instructs me to insert the following script tag on my website, which I have done through inserting the tag inside the <head> tag:
Checkout relies on Stripe.js. To get started, include the following
script tag on your website—it should always be loaded directly from
https://js.stripe.com:
<script src="https://js.stripe.com/v3/"></script>
The next step is where I am having a problem (using the ESNext syntax since this is in a Typescript project):
Next, create an instance of the Stripe object by providing your publishable API key as the first parameter:
const stripe = Stripe('pk_test_sdjxyNjHWmRefdkUNYuS53MA00Ot1f9HOu');
I would like to access Stripe through a service worker, rather than a component directly. However, trying to initialise the stripe instance is not working. I have tried:
importing the Stripe module in various ways, which hasn't worked
adding a dependency on #types/stripe, which seems to prevent the compiler complaining
Currently, my StripeService.ts file has the following code:
const stripe = Stripe("SOME_KEY");
export const redirectToCheckout = (sessionId: string) => {
return stripe.redirectToCheckout(
{
sessionId: sessionId,
});
};
Localhost instance is giving this error:
/src/services/stripe/StripeService.ts
Line 12: 'Stripe' is not defined no-undef
Any suggestions on how I can resolve this issue? I have looked into the react-stripe-elements wrapper, but that is geared towards providing UI components, whereas I only want the Stripe checkout API call behaviour.
Bare Minimum
Minimum implementation is to declare Stripe using any:
declare class Stripe {
constructor(...args: any[]);
redirectToCheckout(...args: any[]): any;
}
const stripe = new Stripe("pk_test_sdjxyNjHWmRefdkUNYuS53MA00Ot1f9HOu");
stripe.redirectToCheckout({
sessionId: sessionId
})
Stronger Typings
You can of course expand this by more explicitly typing the parts that you need:
declare class Stripe {
constructor(publicKey: string);
redirectToCheckout({
sessionId
}: {
sessionId: string;
}): Promise<{ error: Error }>;
}
const stripe = new Stripe("pk_test_sdjxyNjHWmRefdkUNYuS53MA00Ot1f9HOu");
stripe.redirectToCheckout({
sessionId
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
});
Try using the windows object instead:
var stripe = window.Stripe("pk_test_h4naRpZD1t2edp2HQKG2NrZi00rzz5TQJk");
For a service file, you would just add stripe to package.json, then in the file would do:
import Stripe from "stripe";
const stripe = Stripe("SOME_KEY");
export const redirectToCheckout = (sessionId: string) => {
return stripe.redirectToCheckout(
{
sessionId: sessionId,
});
};
You would use the public key in the client side, and the secret key in the server side. You should keep stripe object (Stripe('pk_test_sdjxyNjHWmRefdkUNYuS53MA00Ot1f9HOu')) in your state somehow to be able to retrieve it later.
An example call could be like this:
client side
const {paymentMethod, error} = await this.state.stripe.createPaymentMethod('card', cardElement, {
billing_details: {
name: 'Jenny Rosen',
},
});
StripeService.makePayment(paymentMethod);
server side
import Stripe as "stripe";
const stripe = Stripe("SOME_KEY");
export const makePayment = (paymentMethod: object) => {
...
};

How to authenticate with Google App Engine Admin API from a Google Cloud Function context

I am trying to turn a Google App Engine Flex environment off when not in use, using a Google Cloud Function. I am struggling to find the best way to authenticate using the nodejs GoogleAPIs client.
Surely I don't need user input?
It took a bit of hunting to discover the bundled GoogleAuth library actually picks up the permissions of the Cloud Function environment if you specify the required OAuth scopes. The great thing about this approach is there are no keys to manage!
const {google} = require('googleapis');
const gae = google.appengine('v1');
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/appengine.admin',
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/cloud-platform.read-only']
});
async function trigger(event, context) {
const authClient = await auth.getClient();
const versionsResponse = await gae.apps.services.versions.list({
auth: authClient,
appsId: "<APP_ID>",
servicesId: "<SERVICE_ID>",
fields: "versions(id,name,servingStatus)"
});
// Rest of code ...
}
I found the code here

ReactJS where do I store the API URI?

Where would I store the API URI centrally in a ReactJS Application? The URI only changes between environments and should be easily configurable (i.e. through environment variables).
I have looked into this package and into the new Context API, but am unsure it's the best way to achieve this. I have also looked into dotenv, but I don't like that I would have to use process.env.REACT_APP_SERVICE_URI in every component that wants to access the API. What is the usual approach?
I am not using Redux.
I don't think you need an external dependency to do that.
I usually create simple module called api-client.js, which is responsible for calls to external API and defining endpoints.
In your case you might have:
import axios from 'axios' // some http client lib
const endpoint = process.env.REACT_APP_SERVICE_URI? process.env.REACT_APP_SERVICE_URI : 'https://foo.api.net/'
export default {
getAllProducts () {
return axios.get(endpoint + 'products').then(response => {
log.debug(`api client fetched ${response.data.length} items`)
return response.data
}).catch(err => {
log.error(err.message)
throw err
})
}
},
getProductById (id) {
...
},
}
You read process.env.REACT_APP_SERVICE_URI only once.
I like to put this module inside api directory (and any other API related stuff).

Firebase Cloud Functions Not Running

I'm getting some unexpected behavior from Firebase Cloud Functions where it seems the function below does not run. My expectation is the data in the /posts endpoint will be logged to the console. I get no errors on deploying the function.
The function is for a backend-only action that the client/user is not involved in, so a trigger based on database events or https wont work for me without setting up another server to call the endpoint.
Is there any reason why the below would not log ?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
getScheduledPosts = () => {
admin.database().ref("/posts")
.orderByKey()
.once("value")
.then( (snapshot) => {
console.log(snapshot);
})
.catch(err => {console.log(err)});
console.log("Posts Ran")
}
// Call this function
getScheduledPosts();
You're not defining a Cloud Function at all here. Because you don't have any Cloud Functions defined, the code you've written will never run. You have to export one from your index.js, and its definition has to be built using the firebase-functions SDK. If you're trying to create a database trigger (definitely read the docs there), it looks something like this:
exports.makeUppercase = functions.database.ref('/posts/{id}')
.onWrite(event => {
// do stuff here
})
Don't try to do "one-off" work that should be run when a function is deployed. That's not how Cloud Functions works. Functions are intended to be run in response to events that occur in your project.

Resources