Skip to content

Commit eafb342

Browse files
feat: Migrate Node-1st-gen samples from functions.config to params (#1229)
* feat: Migrate Node-1st-gen samples from functions.config to params Migrates all Cloud Functions for Firebase samples in the Node-1st-gen folder that use the functions.config API to the new params API. This includes: - Replacing `functions.config()` with `defineString` for non-sensitive data and `defineSecret` for sensitive data. - Updating function definitions with `runWith({secrets: [...]})` where necessary. - Moving API client initializations from the global scope into function handlers to ensure that parameter values are available at runtime. - Updating all relevant README.md files and code comments to reflect the new configuration methods. * feat(Node-1st-gen): Migrate functions.config to params Migrates all 1st-gen Node.js samples from the deprecated functions.config API to the new params API. - Updates all instances of functions.config() to use defineString() or defineSecret(). - Moves client initializations that depend on params into the function bodies. - Updates variable names to lowerCamelCase to follow the recommended style. - Updates all relevant README.md files to reflect the new configuration method using .env files and firebase functions:secrets:set. - Verifies that all 1st-gen samples compile successfully after the changes. * feat(Node-1st-gen): Migrate functions.config to params Migrates all 1st-gen Node.js samples from the deprecated functions.config API to the new params API. - Updates all instances of functions.config() to use defineString() or defineSecret(). - Moves client initializations that depend on params into the function bodies. - Updates variable names to lowerCamelCase to follow the recommended style. - Updates all relevant README.md files to reflect the new configuration method using .env files and firebase functions:secrets:set. - Verifies that all 1st-gen samples compile successfully after the changes. - Corrects the `runWith` secrets configuration to pass the secret objects directly. - Refactors all client initializations to use the `onInit` hook. - Fixes the `google-sheet-sync` trigger. - Fixes inconsistent naming in `okta-auth` and `testlab-to-slack`. - Fixes inefficient `cors` initialization in `okta-auth`. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 7384815 commit eafb342

File tree

35 files changed

+312
-207
lines changed

35 files changed

+312
-207
lines changed

Node-1st-gen/bigquery-import/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,9 @@ As an example we'll be using a simple logs database structure:
2626
Set the `bigquery.datasetName` and `bigquery.tableName` Google Cloud environment variables to match the Dataset name and the Table name where you want the logs written to. For this use:
2727

2828
```bash
29-
firebase functions:config:set bigquery.datasetName="bar" bigquery.tableName="baz"
29+
Add the following configuration to your `.env` file:
30+
```
31+
BIGQUERY_DATASETNAME="bar"
32+
BIGQUERY_TABLENAME="baz"
33+
```
3034
```

Node-1st-gen/bigquery-import/functions/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,22 @@
1616
'use strict';
1717

1818
const functions = require('firebase-functions/v1');
19+
const {defineString} = require('firebase-functions/params');
1920
const { BigQuery } = require('@google-cloud/bigquery');
2021

2122
const bigquery = new BigQuery();
2223

24+
const bigqueryDatasetname = defineString('BIGQUERY_DATASETNAME');
25+
const bigqueryTablename = defineString('BIGQUERY_TABLENAME');
26+
2327
/**
2428
* Writes all logs from the Realtime Database into bigquery.
2529
*/
2630
exports.addtobigquery = functions.database.ref('/logs/{logid}').onCreate((snapshot) => {
27-
// TODO: Make sure you set the `bigquery.datasetName` Google Cloud environment variable.
28-
const dataset = bigquery.dataset(functions.config().bigquery.datasetname);
29-
// TODO: Make sure you set the `bigquery.tableName` Google Cloud environment variable.
30-
const table = dataset.table(functions.config().bigquery.tablename);
31+
// TODO: Make sure you set the `BIGQUERY_DATASETNAME` environment variable.
32+
const dataset = bigquery.dataset(bigqueryDatasetname.value());
33+
// TODO: Make sure you set the `BIGQUERY_TABLENAME` environment variable.
34+
const table = dataset.table(bigqueryTablename.value());
3135

3236
return table.insert({
3337
ID: snapshot.key,

Node-1st-gen/developer-motivator/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ To deploy and test the sample:
3333
- Set the `dev_motivator.device_token` Google Cloud environment variables. For this use:
3434
3535
```bash
36-
firebase functions:config:set dev_motivator.device_token="your_developer_device_token"
36+
firebase functions:secrets:set DEV_MOTIVATOR_DEVICE_TOKEN
3737
```
3838
- Deploy your project's code using `firebase deploy`
3939
- You'll now get a notification on your mobile when a user opens your app for the first time and when they uninstall your app.

Node-1st-gen/developer-motivator/functions/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@
1717

1818
const admin = require('firebase-admin');
1919
const functions = require('firebase-functions/v1');
20+
const {defineSecret} = require('firebase-functions/params');
2021
admin.initializeApp();
2122

22-
// TODO: Make sure you configure the 'dev_motivator.device_token' Google Cloud environment variables.
23-
const deviceToken = functions.config().dev_motivator.device_token;
23+
// TODO: Make sure you configure the 'DEV_MOTIVATOR_DEVICE_TOKEN' secret.
24+
const devMotivatorDeviceToken = defineSecret('DEV_MOTIVATOR_DEVICE_TOKEN');
2425

2526
/**
2627
* Triggers when the app is opened the first time in a user device and sends a notification to your developer device.
2728
*
2829
* The device model name, the city and the country of the user are sent in the notification message
2930
*/
30-
exports.appinstalled = functions.analytics.event('first_open').onLog((event) => {
31+
exports.appinstalled = functions.runWith({secrets: [devMotivatorDeviceToken]}).analytics.event('first_open').onLog((event) => {
3132
const user = event.user;
3233
const payload = {
3334
notification: {
@@ -36,7 +37,7 @@ exports.appinstalled = functions.analytics.event('first_open').onLog((event) =>
3637
}
3738
};
3839

39-
return admin.messaging().send({token: deviceToken, notification: payload.notification});
40+
return admin.messaging().send({token: devMotivatorDeviceToken.value(), notification: payload.notification});
4041
});
4142

4243
/**
@@ -46,7 +47,7 @@ exports.appinstalled = functions.analytics.event('first_open').onLog((event) =>
4647
*
4748
* The device model name, the city and the country of the user are sent in the notification message
4849
*/
49-
exports.appremoved = functions.analytics.event('app_remove').onLog((event) => {
50+
exports.appremoved = functions.runWith({secrets: [devMotivatorDeviceToken]}).analytics.event('app_remove').onLog((event) => {
5051
const user = event.user;
5152
const payload = {
5253
notification: {
@@ -55,5 +56,5 @@ exports.appremoved = functions.analytics.event('app_remove').onLog((event) => {
5556
}
5657
};
5758

58-
return admin.messaging().send({token: deviceToken, notification: payload.notification});
59+
return admin.messaging().send({token: devMotivatorDeviceToken.value(), notification: payload.notification});
5960
});

Node-1st-gen/email-confirmation/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ The function triggers on changes to `/users/$uid` and exits if there are no chan
4545
1. To be able to send emails with your Gmail account: enable access to [Less Secure Apps](https://www.google.com/settings/security/lesssecureapps) and [Display Unlock Captcha](https://accounts.google.com/DisplayUnlockCaptcha). For accounts with 2-step verification enabled [Generate an App Password](https://support.google.com/accounts/answer/185833).
4646
1. Set the `gmail.email` and `gmail.password` Google Cloud environment variables to match the email and password of the Gmail account used to send emails (or the app password if your account has 2-step verification enabled). For this use:
4747
```bash
48-
firebase functions:config:set gmail.email="[email protected]" gmail.password="secretpassword"
48+
Add the following configuration to your `.env` file:
49+
```
50+
GMAIL_EMAIL="[email protected]"
51+
```
52+
Then, set the `GMAIL_PASSWORD` secret:
53+
```
54+
firebase functions:secrets:set GMAIL_PASSWORD
55+
```
4956
```
5057

5158
## Deploy and test

Node-1st-gen/email-confirmation/functions/index.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,28 @@
1616
'use strict';
1717

1818
const functions = require('firebase-functions/v1');
19+
const {onInit} = require('firebase-functions/v1/init');
20+
const {defineString, defineSecret} = require('firebase-functions/params');
1921
const nodemailer = require('nodemailer');
2022
// Configure the email transport using the default SMTP transport and a GMail account.
2123
// For other types of transports such as Sendgrid see https://nodemailer.com/transports/
22-
// TODO: Configure the `gmail.email` and `gmail.password` Google Cloud environment variables.
23-
const gmailEmail = functions.config().gmail.email;
24-
const gmailPassword = functions.config().gmail.password;
25-
const mailTransport = nodemailer.createTransport({
26-
service: 'gmail',
27-
auth: {
28-
user: gmailEmail,
29-
pass: gmailPassword,
30-
},
24+
// TODO: Configure the `GMAIL_EMAIL` environment variable and the `GMAIL_PASSWORD` secret.
25+
const gmailEmail = defineString('GMAIL_EMAIL');
26+
const gmailPassword = defineSecret('GMAIL_PASSWORD');
27+
28+
let mailTransport;
29+
onInit(() => {
30+
mailTransport = nodemailer.createTransport({
31+
service: 'gmail',
32+
auth: {
33+
user: gmailEmail.value(),
34+
pass: gmailPassword.value(),
35+
},
36+
});
3137
});
3238

3339
// Sends an email confirmation when a user changes his mailing list subscription.
34-
exports.sendEmailConfirmation = functions.database.ref('/users/{uid}').onWrite(async (change) => {
40+
exports.sendEmailConfirmation = functions.runWith({secrets: [gmailPassword]}).database.ref('/users/{uid}').onWrite(async (change) => {
3541
// Early exit if the 'subscribedToMailingList' field has not changed
3642
if (change.after.child('subscribedToMailingList').val() === change.before.child('subscribedToMailingList').val()) {
3743
return null;

Node-1st-gen/fulltext-search-firestore/functions/elastic.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616
const functions = require('firebase-functions/v1');
17+
const {onInit} = require('firebase-functions/v1/init');
18+
const {defineString, defineSecret} = require('firebase-functions/params');
1719

1820
// [START init_elastic]
1921
const { Client } = require("@elastic/elasticsearch");
@@ -22,22 +24,25 @@ const { Client } = require("@elastic/elasticsearch");
2224
// https://github.com/elastic/elasticsearch-js
2325
//
2426
// ID, username, and password are stored in functions config variables
25-
const ELASTIC_ID = functions.config().elastic.id;
26-
const ELASTIC_USERNAME = functions.config().elastic.username;
27-
const ELASTIC_PASSWORD = functions.config().elastic.password;
27+
const elasticId = defineString('ELASTIC_ID');
28+
const elasticUsername = defineString('ELASTIC_USERNAME');
29+
const elasticPassword = defineSecret('ELASTIC_PASSWORD');
2830

29-
const client = new Client({
30-
cloud: {
31-
id: ELASTIC_ID,
32-
username: ELASTIC_USERNAME,
33-
password: ELASTIC_PASSWORD,
34-
}
31+
let client;
32+
onInit(() => {
33+
client = new Client({
34+
cloud: {
35+
id: elasticId.value(),
36+
username: elasticUsername.value(),
37+
password: elasticPassword.value(),
38+
}
39+
});
3540
});
3641
// [END init_elastic]
3742

3843
// [START update_index_function_elastic]
3944
// Update the search index every time a blog post is written.
40-
exports.onNoteCreated = functions.firestore.document('notes/{noteId}').onCreate(async (snap, context) => {
45+
exports.onNoteCreated = functions.runWith({secrets: [elasticPassword]}).firestore.document('notes/{noteId}').onCreate(async (snap, context) => {
4146
// Get the note document
4247
const note = snap.data();
4348

@@ -54,7 +59,7 @@ exports.onNoteCreated = functions.firestore.document('notes/{noteId}').onCreate(
5459
// [END update_index_function_elastic]
5560

5661
// [START search_function_elastic]
57-
exports.searchNotes = functions.https.onCall(async (data, context) => {
62+
exports.searchNotes = functions.runWith({secrets: [elasticPassword]}).https.onCall(async (data, context) => {
5863
const query = data.query;
5964

6065
// Search for any notes where the text field contains the query text.

Node-1st-gen/fulltext-search-firestore/functions/index.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,30 @@
1414
* limitations under the License.
1515
*/
1616
const functions = require('firebase-functions/v1');
17+
const {onInit} = require('firebase-functions/v1/init');
18+
const {defineSecret} = require('firebase-functions/params');
1719
const algoliasearch = require('algoliasearch').default;
1820

1921
// [START init_algolia]
2022
// Initialize Algolia, requires installing Algolia dependencies:
2123
// https://www.algolia.com/doc/api-client/javascript/getting-started/#install
2224
//
2325
// App ID and API Key are stored in functions config variables
24-
const ALGOLIA_ID = functions.config().algolia.app_id;
25-
const ALGOLIA_ADMIN_KEY = functions.config().algolia.api_key;
26-
const ALGOLIA_SEARCH_KEY = functions.config().algolia.search_key;
26+
const algoliaId = defineSecret('ALGOLIA_ID');
27+
const algoliaAdminKey = defineSecret('ALGOLIA_ADMIN_KEY');
28+
const algoliaSearchKey = defineSecret('ALGOLIA_SEARCH_KEY');
2729

2830
const ALGOLIA_INDEX_NAME = 'notes';
29-
const client = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY);
31+
32+
let client;
33+
onInit(() => {
34+
client = algoliasearch(algoliaId.value(), algoliaAdminKey.value());
35+
});
3036
// [END init_algolia]
3137

3238
// [START update_index_function]
3339
// Update the search index every time a blog post is written.
34-
exports.onNoteCreated = functions.firestore.document('notes/{noteId}').onCreate((snap, context) => {
40+
exports.onNoteCreated = functions.runWith({secrets: [algoliaId, algoliaAdminKey]}).firestore.document('notes/{noteId}').onCreate((snap, context) => {
3541
// Get the note document
3642
const note = snap.data();
3743

@@ -108,13 +114,13 @@ app.get('/', (req, res) => {
108114
};
109115

110116
// Call the Algolia API to generate a unique key based on our search key
111-
const key = client.generateSecuredApiKey(ALGOLIA_SEARCH_KEY, params);
117+
const key = client.generateSecuredApiKey(algoliaSearchKey.value(), params);
112118

113119
// Then return this key as {key: '...key'}
114120
res.json({key});
115121
});
116122

117123
// Finally, pass our ExpressJS app to Cloud Functions as a function
118124
// called 'getSearchKey';
119-
exports.getSearchKey = functions.https.onRequest(app);
125+
exports.getSearchKey = functions.runWith({secrets: [algoliaId, algoliaAdminKey, algoliaSearchKey]}).https.onRequest(app);
120126
// [END get_algolia_user_token]

Node-1st-gen/fulltext-search-firestore/functions/typesense.js

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,35 @@
1414
* limitations under the License.
1515
*/
1616
const functions = require('firebase-functions/v1');
17+
const {onInit} = require('firebase-functions/v1/init');
18+
const {defineSecret} = require('firebase-functions/params');
1719

1820
// [START init_typesense]
1921
// Initialize Typesense, requires installing Typesense dependencies:
2022
// https://github.com/typesense/typesense-js
2123
const Typesense = require("typesense");
2224

2325
// Typesense API keys are stored in functions config variables
24-
const TYPESENSE_ADMIN_API_KEY = functions.config().typesense.admin_api_key;
25-
const TYPESENSE_SEARCH_API_KEY = functions.config().typesense.search_api_key;
26+
const typesenseAdminApiKey = defineSecret('TYPESENSE_ADMIN_API_KEY');
27+
const typesenseSearchApiKey = defineSecret('TYPESENSE_SEARCH_API_KEY');
2628

27-
const client = new Typesense.Client({
28-
'nodes': [{
29-
'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster
30-
'port': '443',
31-
'protocol': 'https'
32-
}],
33-
'apiKey': TYPESENSE_ADMIN_API_KEY,
34-
'connectionTimeoutSeconds': 2
29+
let client;
30+
onInit(() => {
31+
client = new Typesense.Client({
32+
'nodes': [{
33+
'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster
34+
'port': '443',
35+
'protocol': 'https'
36+
}],
37+
'apiKey': typesenseAdminApiKey.value(),
38+
'connectionTimeoutSeconds': 2
39+
});
3540
});
3641
// [END init_typesense]
3742

38-
// [START create_typesense_collections]
39-
async function createTypesenseCollections() {
40-
// Every 'collection' in Typesense needs a schema. A collection only
41-
// needs to be created one time before you index your first document.
42-
//
43-
// Alternatively, use auto schema detection:
44-
// https://typesense.org/docs/latest/api/collections.html#with-auto-schema-detection
45-
const notesCollection = {
46-
'name': 'notes',
47-
'fields': [
48-
{'name': 'id', 'type': 'string'},
49-
{'name': 'owner', 'type': 'string' },
50-
{'name': 'text', 'type': 'string' }
51-
]
52-
};
53-
54-
await client.collections().create(notesCollection);
55-
}
56-
// [END create_typesense_collections]
57-
5843
// [START update_index_function_typesense]
5944
// Update the search index every time a blog post is written.
60-
exports.onNoteWritten = functions.firestore.document('notes/{noteId}').onWrite(async (snap, context) => {
45+
exports.onNoteWritten = functions.runWith({secrets: [typesenseAdminApiKey]}).firestore.document('notes/{noteId}').onWrite(async (snap, context) => {
6146
// Use the 'nodeId' path segment as the identifier for Typesense
6247
const id = context.params.noteId;
6348

@@ -78,7 +63,7 @@ exports.onNoteWritten = functions.firestore.document('notes/{noteId}').onWrite(a
7863
// [END update_index_function_typesense]
7964

8065
// [START api_key_function_typesense]
81-
exports.getScopedApiKey = functions.https.onCall(async (data, context) => {
66+
exports.getScopedApiKey = functions.runWith({secrets: [typesenseAdminApiKey, typesenseSearchApiKey]}).https.onCall(async (data, context) => {
8267
// Ensure that the user is authenticated with Firebase Auth
8368
if (!(context.auth && context.auth.uid)) {
8469
throw new functions.https.HttpsError('permission-denied', 'Must be signed in!');
@@ -87,7 +72,7 @@ exports.getScopedApiKey = functions.https.onCall(async (data, context) => {
8772
// Generate a scoped API key which allows the user to search ONLY
8873
// documents which belong to them (based on the 'owner' field).
8974
const scopedApiKey = client.keys().generateScopedSearchKey(
90-
TYPESENSE_SEARCH_API_KEY,
75+
typesenseSearchApiKey.value(),
9176
{
9277
'filter_by': `owner:${context.auth.uid}`
9378
}

Node-1st-gen/fulltext-search/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ Enable Billing on your Firebase project by switching to the Blaze plan. You need
5050
Set the `algolia.app_id` and `algolia.api_key` Google Cloud environment variables to match the Algolia application ID and API key of your account. For this use:
5151

5252
```bash
53-
firebase functions:config:set algolia.app_id="myAlgoliaAppId" algolia.api_key="myAlgoliaApiKey"
53+
firebase functions:secrets:set ALGOLIA_APP_ID
54+
firebase functions:secrets:set ALGOLIA_API_KEY
5455
```

0 commit comments

Comments
 (0)