diff --git a/starters/nextjs/firebase-ecommerce/.gitignore b/starters/nextjs/firebase-ecommerce/.gitignore index a8219e06..f87b99da 100644 --- a/starters/nextjs/firebase-ecommerce/.gitignore +++ b/starters/nextjs/firebase-ecommerce/.gitignore @@ -40,4 +40,9 @@ vsce-debug.log* # Firebase firebase-debug.log* firebase-debug.* -.firebase \ No newline at end of file +.firebase +.firebaserc + +# dataconnect +dataconnect-generated/ +dataconnect/.dataconnect/ \ No newline at end of file diff --git a/starters/nextjs/firebase-ecommerce/README.md b/starters/nextjs/firebase-ecommerce/README.md index a7cce485..c0666585 100644 --- a/starters/nextjs/firebase-ecommerce/README.md +++ b/starters/nextjs/firebase-ecommerce/README.md @@ -28,8 +28,8 @@ The application provides a quick and easy to use shopping experience with featur 1. Create a new Firebase project in the [Firebase Console](https://console.firebase.google.com). 2. Enable **Email/Password Authentication**. -3. Add Firebase app secrets to `.env.local`. -4. Include Firebase app secrets in **App Hosting**. +3. Replace the relevant commented-out environment variables in `apphosting.yaml` + with your firebase config. #### Firebase Data Connect @@ -37,7 +37,9 @@ The application provides a quick and easy to use shopping experience with featur 2. Create a new Data Connect instance and service. 3. Set up billing for the Firebase project. 4. Switch to the Blaze plan. -5. Define the required schema, queries, and mutations. +5. Modify `dataconnect/dataconnect.yaml` to specify `serviceId`, `location`, + `schema.datasource.postgresql.database` and + `schema.datasource.postgressql.cloudSql.instanceId` 6. Deploy the schema, queries, and mutations to production. #### Firebase Storage @@ -48,13 +50,18 @@ The application provides a quick and easy to use shopping experience with featur #### Firebase App Hosting 1. Connect the Firebase app to your GitHub repository. -2. Create a new backend for the application. -3. Deploy secrets to app hosting using the Firebase CLI. -4. Deploy the application to Firebase Hosting. +2. Create a new backend for the application, but do not deploy yet. + We need to set up environment variables first for the app to work. ### Stripe Setup 1. Create a new Stripe account. +2. Follow the instructions in apphosting.yaml to create the + public and private keys for your stripe application. +3. Create a webhook that listen to "Events on your account" at + /api/stripe/webhook that receives at least + the following events: `payment_intent.succeeded`, `payment_intent.failed`, + `charge.succeeded`, `charge.updated`. 2. Add API keys (Publishable and Secret) to `.env.local`. 3. Set up a webhook endpoint in the Firebase project. 4. Add the webhook secret to `.env.local`. @@ -63,12 +70,18 @@ The application provides a quick and easy to use shopping experience with featur ## Environment Variables -The following environment variables must be configured in `.env.local`: +The following environment variables must be configured in `apphosting.yaml` +(there are comments for where to find these values) -- **Firebase Secrets**: `NEXT_PUBLIC_FIREBASE_API_KEY`, `NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN`, `NEXT_PUBLIC_FIREBASE_PROJECT_ID`, `NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET`, `NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID`, `NEXT_PUBLIC_FIREBASE_APP_ID`. -- **Stripe Secrets**: `NEXT_PUBLIC_STRIPE_PUB_KEY`, `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` +- **Firebase Config**: `NEXT_PUBLIC_FIREBASE_API_KEY`, `NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN`, `NEXT_PUBLIC_FIREBASE_PROJECT_ID`, `NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET`, `NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID`, `NEXT_PUBLIC_FIREBASE_APP_ID`. +- **Stripe Config**: `NEXT_PUBLIC_STRIPE_PUB_KEY`, `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` - **Google/Gemini API Key**: `GOOGLE_API_KEY` +## Deploy +Once you have set up Stripe, Data Connect, and configured your App Hosting environment, it's time to deploy! +If you have automatic rollouts enabled, simply push your changes. Otherwise, after pushing, go to the +Firebase console and click "Create Rollout". + ## Application Features ### Homepage @@ -148,18 +161,17 @@ To run this project locally, follow these steps: ``` npm install ``` -4. Copy the example environment variables: - ``` - cp .env.example .env.local - ``` - • Fill in the required values (Firebase config, Stripe keys, Google API key, etc.). -5. Start the development server: +4. Fill in `apphosting.yaml` as guided by the comments. +5. Initialize the firebase emulator suite with `firebase init emulators`. You may + create an `apphosting.emulator.yaml` if you choose to use different configurations + during local development or you can test against your production config. +5. Start the emulator ``` - npm run dev + firebase emulators:start ``` 6. Open the application in your browser at: ``` - http://localhost:3000 + http://localhost:5002 ``` --- diff --git a/starters/nextjs/firebase-ecommerce/apphosting.yaml b/starters/nextjs/firebase-ecommerce/apphosting.yaml index 867bcae1..070e8cac 100644 --- a/starters/nextjs/firebase-ecommerce/apphosting.yaml +++ b/starters/nextjs/firebase-ecommerce/apphosting.yaml @@ -4,28 +4,46 @@ runConfig: minInstances: 0 # Environment variables and secrets. +# Grant access to secrets in Cloud Secret Manager. +# See https://firebase.google.com/docs/app-hosting/configure#secret-parameters +# If you would like to change any keys' values in local development with the emulator +# (e.g. using Stripe Sandbox keys), you can overwrite values in apphosting.emulator.yaml env: - # Grant access to secrets in Cloud Secret Manager. - # See https://firebase.google.com/docs/app-hosting/configure#secret-parameters - - variable: NEXT_PUBLIC_FIREBASE_API_KEY - secret: FIREBASE_API_KEY - - variable: NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN - secret: FIREBASE_AUTH_DOMAIN - - variable: NEXT_PUBLIC_FIREBASE_PROJECT_ID - secret: FIREBASE_PROJECT_ID - - variable: NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET - secret: FIREBASE_STORAGE_BUCKET - - variable: NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID - secret: FIREBASE_MESSAGING_SENDER_ID - - variable: NEXT_PUBLIC_FIREBASE_APP_ID - secret: FIREBASE_APP_ID - - variable: GOOGLE_API_KEY - secret: GOOGLE_API_KEY - - variable: STRIPE_ACCOUNT_ID - secret: STRIPE_ACCOUNT_ID - - variable: STRIPE_SECRET_KEY - secret: STRIPE_SECRET_KEY - - variable: NEXT_PUBLIC_STRIPE_PUB_KEY - secret: STRIPE_PUB_KEY - - variable: STRIPE_WEBHOOK_SECRET - secret: STRIPE_WEBHOOK_SECRET + # Values for Firebase config can be found by clicking the cog next to "Project Overview" + # in the firebase console and clicking "project settings" + # - variable: NEXT_PUBLIC_FIREBASE_API_KEY + # value: + # - variable: NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN + # value: + # - variable: NEXT_PUBLIC_FIREBASE_PROJECT_ID + # value: + # - variable: NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET + # value: + # - variable: NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID + # value: + # - variable: NEXT_PUBLIC_FIREBASE_APP_ID + # value: + + # The Google API key needs access to the Generative AI APIs. Create your key by visiting + # https://ai.google.dev/gemini-api/docs and clicking "Get a Gemini API Key" + # You can then run `firebase apphosting:secrets:set google-api-key and pasting the value. + # - variable: GOOGLE_API_KEY + # secret: google-api-key + + # To use stripe, you must create an account on stripe.com. From the stripe dashboard + # You can copy the NEXT_PUBLIC_STRIPE_PUB_KEY from "Publishable key" in the dashboard. + # You can set STRIPE_SECRET_KEY by calling `firebase apphosting:secrets:set stripe-secret-key` + # and pasting the value listed as "Secret key" in the dashboard. + # To get the stripe webhook secret, create a stripe webhook as described in README.md, + # go to the webhook details page and click "Reveal" under "Signing secret". Copy that value, + # run `firebase apphosting:secrets:set stripe-webhook-secret` and paste the secret value when + # prompted. The value for STRIPE_ACCOUNT_ID can be found in the upper right corner of the + # webhook details page. + # - variable: NEXT_PUBLIC_STRIPE_PUB_KEY + # value: + # - variable: STRIPE_SECRET_KEY + # secret: stripe-secret-key + # - variable: STRIPE_ACCOUNT_ID + # value: + # - variable: STRIPE_WEBHOOK_SECRET + # secret: stripe-webhook-secret diff --git a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/README.md b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/README.md index 53d46e2e..06a2bf4a 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/README.md +++ b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/README.md @@ -12,7 +12,6 @@ - [*SearchProductTitleUsingL2Similarity*](#searchproducttitleusingl2similarity) - [*SearchProductReviewContentUsingL2Similarity*](#searchproductreviewcontentusingl2similarity) - [*GetOrdersByCustomerId*](#getordersbycustomerid) - - [*GetCurrentCustomerOrders*](#getcurrentcustomerorders) - [*GetOrderById*](#getorderbyid) - [**Mutations**](#mutations) - [*UpsertCustomer*](#upsertcustomer) @@ -988,124 +987,10 @@ Recall that executing the `GetOrdersByCustomerId` query returns a `QueryPromise` The `data` property is an object of type `GetOrdersByCustomerIdData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: ```javascript export interface GetOrdersByCustomerIdData { - orders: ({ - id: UUIDString; - customerId: string; - processedAt: DateString; - chargeId?: string | null; - paymentIntentId?: string | null; - receiptUrl?: string | null; - subtotalPrice: number; - totalPrice: number; - financialStatus: string; - fulfillmentStatus: string; - orderItems_on_order: ({ - id: UUIDString; - quantity: number; - price: number; - product: { - id: UUIDString; - title: string; - handle: string; - productImages_on_product: ({ - url: string; - altText?: string | null; - width: number; - height: number; - })[]; - } & Product_Key; - } & OrderItem_Key)[]; - } & Order_Key)[]; -} -``` -### Using `GetOrdersByCustomerId`'s action shortcut function - -```javascript -import { getDataConnect, DataConnect } from 'firebase/data-connect'; -import { connectorConfig, getOrdersByCustomerId, GetOrdersByCustomerIdVariables } from '@firebasegen/default-connector'; - -// The `GetOrdersByCustomerId` query requires an argument of type `GetOrdersByCustomerIdVariables`: -const getOrdersByCustomerIdVars: GetOrdersByCustomerIdVariables = { - customerId: ..., -}; - -// Call the `getOrdersByCustomerId()` function to execute the query. -// You can use the `await` keyword to wait for the promise to resolve. -const { data } = await getOrdersByCustomerId(getOrdersByCustomerIdVars); -// Variables can be defined inline as well. -const { data } = await getOrdersByCustomerId({ customerId: ..., }); - -// You can also pass in a `DataConnect` instance to the action shortcut function. -const dataConnect = getDataConnect(connectorConfig); -const { data } = await getOrdersByCustomerId(dataConnect, getOrdersByCustomerIdVars); - -console.log(data.orders); - -// Or, you can use the `Promise` API. -getOrdersByCustomerId(getOrdersByCustomerIdVars).then((response) => { - const data = response.data; - console.log(data.orders); -}); -``` - -### Using `GetOrdersByCustomerId`'s `QueryRef` function - -```javascript -import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; -import { connectorConfig, getOrdersByCustomerIdRef, GetOrdersByCustomerIdVariables } from '@firebasegen/default-connector'; - -// The `GetOrdersByCustomerId` query requires an argument of type `GetOrdersByCustomerIdVariables`: -const getOrdersByCustomerIdVars: GetOrdersByCustomerIdVariables = { - customerId: ..., -}; - -// Call the `getOrdersByCustomerIdRef()` function to get a reference to the query. -const ref = getOrdersByCustomerIdRef(getOrdersByCustomerIdVars); -// Variables can be defined inline as well. -const ref = getOrdersByCustomerIdRef({ customerId: ..., }); - -// You can also pass in a `DataConnect` instance to the `QueryRef` function. -const dataConnect = getDataConnect(connectorConfig); -const ref = getOrdersByCustomerIdRef(dataConnect, getOrdersByCustomerIdVars); - -// Call `executeQuery()` on the reference to execute the query. -// You can use the `await` keyword to wait for the promise to resolve. -const { data } = await executeQuery(ref); - -console.log(data.orders); - -// Or, you can use the `Promise` API. -executeQuery(ref).then((response) => { - const data = response.data; - console.log(data.orders); -}); -``` - -## GetCurrentCustomerOrders -You can execute the `GetCurrentCustomerOrders` query using the following action shortcut function, or by calling `executeQuery()` after calling the following `QueryRef` function, both of which are defined in [default-connector/index.d.ts](./index.d.ts): -```javascript -getCurrentCustomerOrders(): QueryPromise; - -getCurrentCustomerOrdersRef(): QueryRef; -``` -You can also pass in a `DataConnect` instance to the action shortcut function or `QueryRef` function. -```javascript -getCurrentCustomerOrders(dc: DataConnect): QueryPromise; - -getCurrentCustomerOrdersRef(dc: DataConnect): QueryRef; -``` - -### Variables -The `GetCurrentCustomerOrders` query has no variables. -### Return Type -Recall that executing the `GetCurrentCustomerOrders` query returns a `QueryPromise` that resolves to an object with a `data` property. - -The `data` property is an object of type `GetCurrentCustomerOrdersData`, which is defined in [default-connector/index.d.ts](./index.d.ts). It has the following fields: -```javascript -export interface GetCurrentCustomerOrdersData { orders?: { orders_on_customer: ({ id: UUIDString; + customerId: string; processedAt: DateString; chargeId?: string | null; paymentIntentId?: string | null; @@ -1134,43 +1019,55 @@ export interface GetCurrentCustomerOrdersData { }; } ``` -### Using `GetCurrentCustomerOrders`'s action shortcut function +### Using `GetOrdersByCustomerId`'s action shortcut function ```javascript import { getDataConnect, DataConnect } from 'firebase/data-connect'; -import { connectorConfig, getCurrentCustomerOrders } from '@firebasegen/default-connector'; +import { connectorConfig, getOrdersByCustomerId, GetOrdersByCustomerIdVariables } from '@firebasegen/default-connector'; +// The `GetOrdersByCustomerId` query requires an argument of type `GetOrdersByCustomerIdVariables`: +const getOrdersByCustomerIdVars: GetOrdersByCustomerIdVariables = { + customerId: ..., +}; -// Call the `getCurrentCustomerOrders()` function to execute the query. +// Call the `getOrdersByCustomerId()` function to execute the query. // You can use the `await` keyword to wait for the promise to resolve. -const { data } = await getCurrentCustomerOrders(); +const { data } = await getOrdersByCustomerId(getOrdersByCustomerIdVars); +// Variables can be defined inline as well. +const { data } = await getOrdersByCustomerId({ customerId: ..., }); // You can also pass in a `DataConnect` instance to the action shortcut function. const dataConnect = getDataConnect(connectorConfig); -const { data } = await getCurrentCustomerOrders(dataConnect); +const { data } = await getOrdersByCustomerId(dataConnect, getOrdersByCustomerIdVars); console.log(data.orders); // Or, you can use the `Promise` API. -getCurrentCustomerOrders().then((response) => { +getOrdersByCustomerId(getOrdersByCustomerIdVars).then((response) => { const data = response.data; console.log(data.orders); }); ``` -### Using `GetCurrentCustomerOrders`'s `QueryRef` function +### Using `GetOrdersByCustomerId`'s `QueryRef` function ```javascript import { getDataConnect, DataConnect, executeQuery } from 'firebase/data-connect'; -import { connectorConfig, getCurrentCustomerOrdersRef } from '@firebasegen/default-connector'; +import { connectorConfig, getOrdersByCustomerIdRef, GetOrdersByCustomerIdVariables } from '@firebasegen/default-connector'; +// The `GetOrdersByCustomerId` query requires an argument of type `GetOrdersByCustomerIdVariables`: +const getOrdersByCustomerIdVars: GetOrdersByCustomerIdVariables = { + customerId: ..., +}; -// Call the `getCurrentCustomerOrdersRef()` function to get a reference to the query. -const ref = getCurrentCustomerOrdersRef(); +// Call the `getOrdersByCustomerIdRef()` function to get a reference to the query. +const ref = getOrdersByCustomerIdRef(getOrdersByCustomerIdVars); +// Variables can be defined inline as well. +const ref = getOrdersByCustomerIdRef({ customerId: ..., }); // You can also pass in a `DataConnect` instance to the `QueryRef` function. const dataConnect = getDataConnect(connectorConfig); -const ref = getCurrentCustomerOrdersRef(dataConnect); +const ref = getOrdersByCustomerIdRef(dataConnect, getOrdersByCustomerIdVars); // Call `executeQuery()` on the reference to execute the query. // You can use the `await` keyword to wait for the promise to resolve. diff --git a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/esm/index.esm.js b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/esm/index.esm.js index 0762e9ca..eb6c0c57 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/esm/index.esm.js +++ b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/esm/index.esm.js @@ -156,16 +156,6 @@ export function getOrdersByCustomerId(dcOrVars, vars) { return executeQuery(getOrdersByCustomerIdRef(dcOrVars, vars)); } -export function getCurrentCustomerOrdersRef(dc) { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetCurrentCustomerOrders'); -} - -export function getCurrentCustomerOrders(dc) { - return executeQuery(getCurrentCustomerOrdersRef(dc)); -} - export function getOrderByIdRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); diff --git a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.cjs.js b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.cjs.js index beeb0882..b1a652be 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.cjs.js +++ b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.cjs.js @@ -127,14 +127,6 @@ exports.getOrdersByCustomerIdRef = function getOrdersByCustomerIdRef(dcOrVars, v exports.getOrdersByCustomerId = function getOrdersByCustomerId(dcOrVars, vars) { return executeQuery(getOrdersByCustomerIdRef(dcOrVars, vars)); }; -exports.getCurrentCustomerOrdersRef = function getCurrentCustomerOrdersRef(dc) { - const { dc: dcInstance} = validateArgs(connectorConfig, dc, undefined); - dcInstance._useGeneratedSdk(); - return queryRef(dcInstance, 'GetCurrentCustomerOrders'); -} -exports.getCurrentCustomerOrders = function getCurrentCustomerOrders(dc) { - return executeQuery(getCurrentCustomerOrdersRef(dc)); -}; exports.getOrderByIdRef = function getOrderByIdRef(dcOrVars, vars) { const { dc: dcInstance, vars: inputVars} = validateArgs(connectorConfig, dcOrVars, vars, true); dcInstance._useGeneratedSdk(); diff --git a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.d.ts b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.d.ts index 029fb637..d8e9fb25 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.d.ts +++ b/starters/nextjs/firebase-ecommerce/dataconnect-generated/js/default-connector/index.d.ts @@ -150,38 +150,6 @@ export interface GetCollectionsByPageVariables { page?: string | null; } -export interface GetCurrentCustomerOrdersData { - orders?: { - orders_on_customer: ({ - id: UUIDString; - processedAt: DateString; - chargeId?: string | null; - paymentIntentId?: string | null; - receiptUrl?: string | null; - subtotalPrice: number; - totalPrice: number; - financialStatus: string; - fulfillmentStatus: string; - orderItems_on_order: ({ - id: UUIDString; - quantity: number; - price: number; - product: { - id: UUIDString; - title: string; - handle: string; - productImages_on_product: ({ - url: string; - altText?: string | null; - width: number; - height: number; - })[]; - } & Product_Key; - } & OrderItem_Key)[]; - } & Order_Key)[]; - }; -} - export interface GetOrderByIdData { order?: { id: UUIDString; @@ -215,34 +183,36 @@ export interface GetOrderByIdVariables { } export interface GetOrdersByCustomerIdData { - orders: ({ - id: UUIDString; - customerId: string; - processedAt: DateString; - chargeId?: string | null; - paymentIntentId?: string | null; - receiptUrl?: string | null; - subtotalPrice: number; - totalPrice: number; - financialStatus: string; - fulfillmentStatus: string; - orderItems_on_order: ({ + orders?: { + orders_on_customer: ({ id: UUIDString; - quantity: number; - price: number; - product: { + customerId: string; + processedAt: DateString; + chargeId?: string | null; + paymentIntentId?: string | null; + receiptUrl?: string | null; + subtotalPrice: number; + totalPrice: number; + financialStatus: string; + fulfillmentStatus: string; + orderItems_on_order: ({ id: UUIDString; - title: string; - handle: string; - productImages_on_product: ({ - url: string; - altText?: string | null; - width: number; - height: number; - })[]; - } & Product_Key; - } & OrderItem_Key)[]; - } & Order_Key)[]; + quantity: number; + price: number; + product: { + id: UUIDString; + title: string; + handle: string; + productImages_on_product: ({ + url: string; + altText?: string | null; + width: number; + height: number; + })[]; + } & Product_Key; + } & OrderItem_Key)[]; + } & Order_Key)[]; + }; } export interface GetOrdersByCustomerIdVariables { @@ -570,14 +540,6 @@ export function getOrdersByCustomerIdRef(dc: DataConnect, vars: GetOrdersByCusto export function getOrdersByCustomerId(vars: GetOrdersByCustomerIdVariables): QueryPromise; export function getOrdersByCustomerId(dc: DataConnect, vars: GetOrdersByCustomerIdVariables): QueryPromise; -/* Allow users to create refs without passing in DataConnect */ -export function getCurrentCustomerOrdersRef(): QueryRef; -/* Allow users to pass in custom DataConnect instances */ -export function getCurrentCustomerOrdersRef(dc: DataConnect): QueryRef; - -export function getCurrentCustomerOrders(): QueryPromise; -export function getCurrentCustomerOrders(dc: DataConnect): QueryPromise; - /* Allow users to create refs without passing in DataConnect */ export function getOrderByIdRef(vars: GetOrderByIdVariables): QueryRef; /* Allow users to pass in custom DataConnect instances */ diff --git a/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/main/input.gql b/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/main/input.gql index 3275e7b7..9d82f8b6 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/main/input.gql +++ b/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/main/input.gql @@ -571,17 +571,33 @@ input Order_Data { """ subtotalPrice: Float """ + ✨ `_expr` server value variant of `subtotalPrice` (✨ Generated from Field `Order`.`subtotalPrice` of type `Float!`) + """ + subtotalPrice_expr: Float_Expr + """ ✨ Generated from Field `Order`.`totalPrice` of type `Float!` """ totalPrice: Float """ + ✨ `_expr` server value variant of `totalPrice` (✨ Generated from Field `Order`.`totalPrice` of type `Float!`) + """ + totalPrice_expr: Float_Expr + """ ✨ Generated from Field `Order`.`totalShippingPrice` of type `Float!` """ totalShippingPrice: Float """ + ✨ `_expr` server value variant of `totalShippingPrice` (✨ Generated from Field `Order`.`totalShippingPrice` of type `Float!`) + """ + totalShippingPrice_expr: Float_Expr + """ ✨ Generated from Field `Order`.`totalTax` of type `Float!` """ totalTax: Float + """ + ✨ `_expr` server value variant of `totalTax` (✨ Generated from Field `Order`.`totalTax` of type `Float!`) + """ + totalTax_expr: Float_Expr } """ ✨ Generated filter input type for table 'Order'. This input allows filtering objects using various conditions. Use `_or`, `_and`, and `_not` to compose complex filters. @@ -793,6 +809,10 @@ input OrderItem_Data { """ price: Float """ + ✨ `_expr` server value variant of `price` (✨ Generated from Field `OrderItem`.`price` of type `Float!`) + """ + price_expr: Float_Expr + """ ✨ Generated from Field `OrderItem`.`quantity` of type `Int!` """ quantity: Int @@ -1981,6 +2001,10 @@ input ProductVariant_Data { """ price: Float """ + ✨ `_expr` server value variant of `price` (✨ Generated from Field `ProductVariant`.`price` of type `Float!`) + """ + price_expr: Float_Expr + """ ✨ Generated from Field `ProductVariant`.`sku` of type `String` """ sku: String diff --git a/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/prelude.gql b/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/prelude.gql index b8f87f7a..79801a62 100644 --- a/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/prelude.gql +++ b/starters/nextjs/firebase-ecommerce/dataconnect/.dataconnect/schema/prelude.gql @@ -198,9 +198,19 @@ input UUID_Filter { "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." isNull: Boolean "Match if field is exactly equal to provided value." - eq: UUID + eq: UUID @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: UUID_Expr @fdc_oneOf(group: "eq") "Match if field is not equal to provided value." - ne: UUID + ne: UUID @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: UUID_Expr @fdc_oneOf(group: "ne") "Match if field value is among the provided list of values." in: [UUID!] "Match if field value is not among the provided list of values." @@ -322,21 +332,51 @@ input Float_Filter { "When true, match if field `IS NULL`. When false, match if field is `NOT NULL`." isNull: Boolean "Match if field is exactly equal to provided value." - eq: Float + eq: Float @fdc_oneOf(group: "eq") + """ + Match if field is exactly equal to the result of the provided server value + expression. + """ + eq_expr: Float_Expr @fdc_oneOf(group: "eq") "Match if field is not equal to provided value." - ne: Float + ne: Float @fdc_oneOf(group: "ne") + """ + Match if field is not equal to the result of the provided server value + expression. + """ + ne_expr: Float_Expr @fdc_oneOf(group: "ne") "Match if field value is among the provided list of values." in: [Float!] "Match if field value is not among the provided list of values." nin: [Float!] "Match if field value is greater than the provided value." - gt: Float + gt: Float @fdc_oneOf(group: "gt") + """ + Match if field value is greater than the result of the provided server value + expression. + """ + gt_expr: Float_Expr @fdc_oneOf(group: "gt") "Match if field value is greater than or equal to the provided value." - ge: Float + ge: Float @fdc_oneOf(group: "ge") + """ + Match if field value is greater than or equal to the result of the provided + server value expression. + """ + ge_expr: Float_Expr @fdc_oneOf(group: "ge") "Match if field value is less than the provided value." - lt: Float + lt: Float @fdc_oneOf(group: "lt") + """ + Match if field value is less than the result of the provided server value + expression. + """ + lt_expr: Float_Expr @fdc_oneOf(group: "lt") "Match if field value is less than or equal to the provided value." - le: Float + le: Float @fdc_oneOf(group: "le") + """ + Match if field value is less than or equal to the result of the provided + server value expression. + """ + le_expr: Float_Expr @fdc_oneOf(group: "le") } "Query filter criteria for `[Float!]` scalar fields." @@ -722,8 +762,6 @@ scalar UUID_Expr """ A Common Expression Language (CEL) expression that returns a Int at runtime. - -**Limitation**: Currently, only a limited set of expressions are supported. """ scalar Int_Expr @specifiedBy(url: "https://github.com/google/cel-spec") @@ -732,6 +770,17 @@ scalar Int_Expr @fdc_forbiddenAsFieldType @fdc_example(value: "2 * 4", description: "Evaluates to 8.") @fdc_example(value: "vars.foo.size()", description: "Assuming `vars.foo` is a string, it will evaluate to the length of the string.") + +""" +A Common Expression Language (CEL) expression that returns a Float at runtime. +""" +scalar Float_Expr + @specifiedBy(url: "https://github.com/google/cel-spec") + @fdc_celExpression(returnType: "float") + @fdc_forbiddenAsVariableType + @fdc_forbiddenAsFieldType + @fdc_example(value: "2.0 * 4.0", description: "Evaluates to 8.0.") + """ A Common Expression Language (CEL) expression whose return type is unspecified. @@ -1640,54 +1689,34 @@ input Timestamp_ListFilter { "Update input of a `Date` value." input Date_Update { - "Set the field to the provided date." - set: Date @fdc_oneOf(group: "set") - "Set the field to the provided date CEL expression." - set_expr: Date_Expr @fdc_oneOf(group: "set") - "Set the field to the provided relative date." - set_date: Date_Relative @fdc_oneOf(group: "set") + "Increment the field by a provided duration." + inc: Date_Duration @fdc_oneOf + "Decrement the field by a provided duration." + dec: Date_Duration @fdc_oneOf } "Update input of a `Date` list value." input Date_ListUpdate { - "Replace the current list with the provided list of `Date` values." - set: [Date!] "Append the provided `Date` values to the existing list." - append: [Date!] + append: [Date!] @fdc_oneOf "Prepend the provided `Date` values to the existing list." - prepend: [Date!] - "Remove the date value at the specified index." - delete: Int - "The index of the list to perform updates." - i: Int - "Update the date value at the specified index." - update: Date + prepend: [Date!] @fdc_oneOf } "Update input of a `Timestamp` value." input Timestamp_Update { - "Set the field to the provided timestamp." - set: Timestamp @fdc_oneOf(group: "set") - "Set the field to the provided timestamp CEL expression." - set_expr: Timestamp_Expr @fdc_oneOf(group: "set") - "Set the field to the provided relative timestamp." - set_time: Timestamp_Relative @fdc_oneOf(group: "set") + "Increment the field by a provided duration." + inc: Timestamp_Duration @fdc_oneOf + "Decrement the field by a provided duration." + dec: Timestamp_Duration @fdc_oneOf } "Update input of an `Timestamp` list value." input Timestamp_ListUpdate { - "Replace the current list with the provided list of `Timestamp` values." - set: [Timestamp!] "Append the provided `Timestamp` values to the existing list." - append: [Timestamp!] + append: [Timestamp!] @fdc_oneOf "Prepend the provided `Timestamp` values to the existing list." - prepend: [Timestamp!] - "Remove the timestamp value at the specified index." - delete: Int - "The index of the list to perform updates." - i: Int - "Update the timestamp value at the specified index." - update: Timestamp + prepend: [Timestamp!] @fdc_oneOf } @@ -1775,132 +1804,84 @@ enum Date_Interval @fdc_forbiddenAsFieldType { YEAR } -"Update input of a `String` value." -input String_Update { - "Set the field to a provided value." - set: String @fdc_oneOf(group: "set") - "Set the field to a provided server value expression." - set_expr: String_Expr @fdc_oneOf(group: "set") -} - "Update input of a `String` list value." input String_ListUpdate { - "Set the list with the provided values." - set: [String!] "Append the provided values to the existing list." - append: [String!] + append: [String!] @fdc_oneOf "Prepend the provided values to the existing list." - prepend: [String!] -} - -"Update input of a `UUID` value." -input UUID_Update { - "Set the field to a provided UUID." - set: UUID @fdc_oneOf(group: "set") - "Set the field to a provided UUID expression." - set_expr: UUID_Expr @fdc_oneOf(group: "set") + prepend: [String!] @fdc_oneOf } "Update input of an `ID` list value." input UUID_ListUpdate { - "Set the list with the provided list of UUIDs." - set: [UUID!] "Append the provided UUIDs to the existing list." - append: [UUID!] + append: [UUID!] @fdc_oneOf "Prepend the provided UUIDs to the existing list." - prepend: [UUID!] + prepend: [UUID!] @fdc_oneOf } "Update input of an `Int` value." input Int_Update { - "Set the field to a provided value." - set: Int "Increment the field by a provided value." - inc: Int + inc: Int @fdc_oneOf "Decrement the field by a provided value." - dec: Int + dec: Int @fdc_oneOf } "Update input of an `Int` list value." input Int_ListUpdate { - "Set the list with the provided values." - set: [Int!] "Append the provided list of values to the existing list." - append: [Int!] + append: [Int!] @fdc_oneOf "Prepend the provided list of values to the existing list." - prepend: [Int!] + prepend: [Int!] @fdc_oneOf } "Update input of an `Int64` value." input Int64_Update { - "Set the field to a provided value." - set: Int64 "Increment the field by a provided value." - inc: Int64 + inc: Int64 @fdc_oneOf "Decrement the field by a provided value." - dec: Int64 + dec: Int64 @fdc_oneOf } "Update input of an `Int64` list value." input Int64_ListUpdate { - "Replace the list with the provided values." - set: [Int64!] "Append the provided list of values to the existing list." - append: [Int64!] + append: [Int64!] @fdc_oneOf "Prepend the provided list of values to the existing list." - prepend: [Int64!] + prepend: [Int64!] @fdc_oneOf } "Update input of a `Float` value." input Float_Update { - "Set the field to a provided value." - set: Float "Increment the field by a provided value." - inc: Float + inc: Float @fdc_oneOf "Decrement the field by a provided value." - dec: Float + dec: Float @fdc_oneOf } "Update input of a `Float` list value." input Float_ListUpdate { - "Set the list with the provided values." - set: [Float!] "Append the provided list of values to the existing list." - append: [Float!] + append: [Float!] @fdc_oneOf "Prepend the provided list of values to the existing list." - prepend: [Float!] -} - -"Update input of a `Boolean` value." -input Boolean_Update { - "Set the field to a provided value." - set: Boolean + prepend: [Float!] @fdc_oneOf } "Update input of a `Boolean` list value." input Boolean_ListUpdate { - "Set the list with the provided values." - set: [Boolean!] "Append the provided list of values to the existing list." - append: [Boolean!] + append: [Boolean!] @fdc_oneOf "Prepend the provided list of values to the existing list." - prepend: [Boolean!] -} - -"Update input of an `Any` value." -input Any_Update { - "Set the field to a provided value." - set: Any + prepend: [Boolean!] @fdc_oneOf } "Update input of an `Any` list value." input Any_ListUpdate { - "Set the list with the provided values." - set: [Any!] "Append the provided list of values to the existing list." - append: [Any!] + append: [Any!] @fdc_oneOf "Prepend the provided list of values to the existing list." - prepend: [Any!] + prepend: [Any!] @fdc_oneOf } type Query { @@ -1963,31 +1944,6 @@ input Vector_ListFilter { excludesAll: [Vector!] } -"Update input of a Vector value." -input Vector_Update { - "Set the field to the provided vector value." - set: Vector @fdc_oneOf(group: "set") - "Set the field to the vector embedding result from a text input." - set_embed: Vector_Embed @fdc_oneOf(group: "set") -} - - -"Update input of a Vector list value." -input Vector_ListUpdate { - "Replace the current list with the provided list of Vector values." - set: [Vector] - "Append the provided Vector values to the existing list." - append: [Vector] - "Prepend the provided Vector values to the existing list." - prepend: [Vector] - "Delete the vector at the specified index." - delete: Int - "The index of the vector to be updated." - i: Int - "Update the vector at the specified index." - update: Vector -} - """ Create a vector embedding of text using the given model on Vertex AI. diff --git a/starters/nextjs/firebase-ecommerce/firebase.json b/starters/nextjs/firebase-ecommerce/firebase.json index 73f59971..7331b174 100644 --- a/starters/nextjs/firebase-ecommerce/firebase.json +++ b/starters/nextjs/firebase-ecommerce/firebase.json @@ -1,5 +1,23 @@ { "dataconnect": { "source": "dataconnect" + }, + "emulators": { + "apphosting": { + "port": 5002, + "rootDirectory": "./", + "startCommand": "pnpm run dev" + }, + "auth": { + "port": 9099 + }, + "dataconnect": { + "port": 9399, + "dataDir": "dataconnect/.dataconnect/pgliteData" + }, + "ui": { + "enabled": true + }, + "singleProjectMode": true } }