# Google Cloud Functions

Run BoxLang handlers on Google Cloud Functions Gen 2 using the Java 21 runtime and the BoxLang GCF bridge entry point: `ortus.boxlang.runtime.gcp.FunctionRunner`.

This page matches the current starter project and runtime behavior.

## 🚀 What You Get

* BoxLang handler files in `src/main/bx`
* Convention-based routing from URI to handler class
* Local HTTP server via the Google Functions Java Invoker
* Deployable ZIP with handlers, config, modules, and runtime JAR

## 🏛️ Runtime + Starter Architecture

This experience is intentionally a combination of two projects:

* **Starter project** (`boxlang-starter-google-functions`): your app shell, handlers, tests, and Gradle tasks
* **Runtime project** (`boxlang-google-functions`): the Java bridge that adapts GCF HTTP requests to BoxLang execution

The runtime entry point is:

* `ortus.boxlang.runtime.gcp.FunctionRunner`

At execution time, the runtime handles:

* HTTP request mapping into a BoxLang-friendly `event` struct
* route resolution from URI to `.bx` handler file
* class compilation/loading and warm-invocation class caching
* method dispatch (default `run()` or `x-bx-function` override)
* response mapping from BoxLang `response` struct to GCF HTTP output

This split lets you keep all business logic in BoxLang files while using Java only as the serverless runtime bridge.

## 🔬 Runtime Execution Flow

```
HTTP Request
    -> Google Cloud Functions Gen 2 (java21)
    -> FunctionRunner (HttpFunction)
    -> RequestMapper (HttpRequest -> event struct)
    -> Route resolution (first URI segment -> PascalCase -> .bx file)
    -> Handler compilation/load (cached on warm invocations)
    -> Method resolution (x-bx-function header or run)
    -> BoxLang handler method execution
    -> ResponseMapper (response struct -> HttpResponse)
```

## ⚡ Cold Start, Warm Start, and Debug Mode

* **Cold start:** runtime initializes in the container before first request handling.
* **Warm invocations:** compiled handler classes are reused to avoid repeated compilation work.
* **Debug mode (`BOXLANG_GCP_DEBUGMODE=true`):** class caching is disabled so handler edits are picked up quickly during development.
* **Production guidance:** keep debug mode off for best performance.

## 🔁 Portability Notes

The runtime keeps the handler contract familiar across serverless targets. In practice, many handler patterns can be shared between BoxLang AWS Lambda and BoxLang GCF with minimal changes.

## ✅ Requirements

| Requirement                 | Version          |
| --------------------------- | ---------------- |
| Java                        | 21               |
| Google Cloud SDK (`gcloud`) | Latest           |
| Gradle                      | Wrapper included |

## 📦 Starter Project Setup

Use the official starter:

```bash
git clone https://github.com/ortus-boxlang/boxlang-starter-google-functions.git
cd boxlang-starter-google-functions
```

Run tests once to verify your environment:

```bash
./gradlew clean test
```

## 🧪 Launch Locally

Start the local function server:

```bash
./gradlew runFunction
```

By default it runs on port `9099` and uses `src/main/bx` as the function root.

Try a request:

```bash
curl http://localhost:9099/
```

Call a specific method via header:

```bash
curl -H "x-bx-function: anotherLambda" http://localhost:9099/
```

Send JSON payload:

```bash
curl -X POST http://localhost:9099/ \
  -H "Content-Type: application/json" \
  -d @workbench/sampleRequests/event-local.json
```

### Local overrides

```bash
./gradlew runFunction -PtestPort=8080
./gradlew runFunction -PdebugMode=true
./gradlew runFunction -PfunctionRoot=/absolute/path/to/bx/files
```

### Local `runFunction` Runner Details

The starter's `runFunction` Gradle task uses the official Google Functions Java Invoker and wires it to the BoxLang runtime entry point.

What it does:

* launches `com.google.cloud.functions.invoker.runner.Invoker`
* passes `ortus.boxlang.runtime.gcp.FunctionRunner` as the target
* sets `BOXLANG_GCP_ROOT` to your function root (default: `src/main/bx`)
* sets `BOXLANG_GCP_DEBUGMODE` from `-PdebugMode` or defaults
* runs on `-PtestPort` (default `9099`)

Expected startup banner:

```
================================================================
 BoxLang GCF Function Invoker
 Listening on  : http://localhost:9099
 Function root : .../src/main/bx
 Debug mode    : true|false
 Press Ctrl+C to stop.
================================================================
```

Runner troubleshooting:

* **Port in use:** start with `-PtestPort=8080`
* **Wrong handlers loaded:** point to the right folder with `-PfunctionRoot=...`
* **Code changes not reflected:** use `-PdebugMode=true` during development
* **Missing default route:** ensure `Lambda.bx` exists in the configured function root

## 🧩 Handler Contract

Each handler method receives:

* `event`: HTTP request data mapped into a struct
* `context`: function metadata (name, revision, project, request id)
* `response`: mutable response struct (`statusCode`, `headers`, `body`, `cookies`)

Default method is `run()`.

### Event Struct Shape

The runtime maps each incoming HTTP request into an `event` struct designed for serverless portability.

```js
{
    method: "GET",
    path: "/products/42",
    rawPath: "/products/42",
    headers: {
        "content-type": "application/json"
    },
    queryStringParameters: {
        page: "1"
    },
    body: "",
    requestContext: {
        http: {
            method: "GET",
            path: "/products/42"
        }
    }
}
```

### Context Struct Shape

The `context` struct contains runtime metadata:

* `functionName`
* `functionVersion`
* `projectId`
* `requestId`

### Response Struct Shape

The runtime provides a mutable `response` struct:

```js
{
    statusCode: 200,
    headers: {
        "Content-Type": "application/json"
    },
    body: "",
    cookies: []
}
```

Response behavior:

* Returning a struct or array from your method will be JSON serialized.
* Returning a plain string writes that string as the response body.
* Writing to `response.body` gives explicit control over output.

### Example handler

```js
class {

    function run( event, context, response ) {
        response.statusCode = 200
        response.body = {
            "error": false,
            "messages": [],
            "data": "====> Incoming event " & event.toString()
        }
    }

    function anotherLambda( event, context, response ) {
        return "Hola!!"
    }

}
```

## 🛣️ Convention-Based Routing

The runtime supports multi-routing by resolving the first URI path segment into a PascalCase `.bx` handler file.

Routing algorithm:

1. Read the first path segment from the request URI.
2. Convert it to PascalCase.
3. Look for `<Segment>.bx` under `BOXLANG_GCP_ROOT`.
4. Fall back to `Lambda.bx` if no file is found.

### URI to Handler Mapping

| Request URI      | Resolved Handler  |
| ---------------- | ----------------- |
| `/`              | `Lambda.bx`       |
| `/customers`     | `Customers.bx`    |
| `/customers/123` | `Customers.bx`    |
| `/products`      | `Products.bx`     |
| `/user-profiles` | `UserProfiles.bx` |
| `/api_endpoints` | `ApiEndpoints.bx` |
| `/unknown`       | `Lambda.bx`       |

### Multi-Handler Layout

```
src/main/bx/
  Application.bx
  Lambda.bx        # fallback and root route
  Customers.bx     # handles /customers/**
  Products.bx      # handles /products/**
  UserProfiles.bx  # handles /user-profiles/**
```

The first segment selects the class. Remaining URI segments are still available via `event.path` for your own parsing.

### Multi-Routing Handler Example

```js
class {

    function run( event, context, response ) {
        response.statusCode = 200
        response.body = {
            "error": false,
            "resource": "customers",
            "path": event.path,
            "method": event.method
        }
    }

    function findById( event, context, response ) {
        response.statusCode = 200
        response.body = {
            "error": false,
            "action": "findById",
            "path": event.path
        }
    }

}
```

## 🔀 Method Routing with x-bx-function

After URI routing resolves the handler class, method routing can choose which function to invoke.

* Without header, runtime calls `run()`.
* With header, runtime attempts the named method.

```bash
# Lambda.bx::run()
curl http://localhost:9099/

# Lambda.bx::anotherLambda()
curl -H "x-bx-function: anotherLambda" http://localhost:9099/

# Customers.bx::findById() when /customers resolves class
curl -H "x-bx-function: findById" http://localhost:9099/customers/123
```

This gives you two dispatch layers:

* URI path selects the handler class
* `x-bx-function` selects the method in that class

## ⚙️ Runtime Environment Variables

| Variable                | Purpose                                          |
| ----------------------- | ------------------------------------------------ |
| `BOXLANG_GCP_ROOT`      | Root directory for `.bx` handlers                |
| `BOXLANG_GCP_CLASS`     | Override default handler path                    |
| `BOXLANG_GCP_DEBUGMODE` | Enable verbose logging and disable class caching |
| `BOXLANG_GCP_CONFIG`    | Custom `boxlang.json` path                       |
| `K_SERVICE`             | Function name (set by GCF)                       |
| `K_REVISION`            | Function revision (set by GCF)                   |
| `GOOGLE_CLOUD_PROJECT`  | Project ID (set by GCF)                          |

## 🏗️ Build Deployable Artifacts

Build the GCF package:

```bash
./gradlew clean shadowJar buildLambdaZip
```

Output ZIP:

* `build/distributions/boxlang-google-function-project-<version>.zip`

The ZIP includes:

* `.bx` handlers at ZIP root
* `boxlang.json`
* `boxlang_modules/`
* `lib/` with runtime and dependencies

## ☁️ Deploy to Google Cloud Functions Gen 2

Authenticate and select project:

```bash
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
```

Deploy:

```bash
gcloud functions deploy YOUR_FUNCTION_NAME \
  --gen2 \
  --runtime=java21 \
  --region=us-central1 \
  --entry-point=ortus.boxlang.runtime.gcp.FunctionRunner \
  --trigger-http \
  --allow-unauthenticated \
  --source=build/distributions/boxlang-google-function-project-1.0.0.zip
```

If you changed `version` in `gradle.properties`, update the ZIP filename in `--source`.

## 🧪 Testing

Run tests:

```bash
./gradlew test
```

Run the integration test class:

```bash
./gradlew test --tests "com.myproject.FunctionRunnerTest"
```

Open report:

* `build/reports/tests/test/index.html`

### Unit and Integration Tests Shipped in the Starter

The starter currently ships one primary integration test class and two GCF HTTP mock classes:

| File                                                      | Purpose                                                         |
| --------------------------------------------------------- | --------------------------------------------------------------- |
| `src/test/java/com/myproject/FunctionRunnerTest.java`     | End-to-end tests of request -> BoxLang handler -> HTTP response |
| `src/test/java/com/myproject/mocks/MockHttpRequest.java`  | Fluent test request builder implementing GCF `HttpRequest`      |
| `src/test/java/com/myproject/mocks/MockHttpResponse.java` | Captures status/body/headers implementing GCF `HttpResponse`    |

`FunctionRunnerTest` validates:

* missing handler path error behavior
* default `run()` execution and JSON response assertions
* `x-bx-function` method routing (`anotherLambda`)
* response headers (`Content-Type`)
* empty headers and large-body handling
* query parameter handling
* concurrent invocation behavior
* core accessor behavior (`getDefaultFunctionPath`, `inDebugMode`, `getRuntime`)

Run only the shipped integration test:

```bash
./gradlew test --tests "com.myproject.FunctionRunnerTest"
```

## 📝 Notes

* The starter currently keeps handlers in `src/main/bx`, not `src/main/resources`.
* Local launch uses `-PtestPort` and `-PdebugMode` flags.
* For this starter, deployment source is the generated ZIP in `build/distributions`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://boxlang.ortusbooks.com/getting-started/running-boxlang/google-cloud-functions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
