Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ EXAMPLES_DIR := examples
COMPUTE_WIT := wit/deps/fastly/compute.wit

# Define all available examples (add new ones here)
EXAMPLES := bottle-app flask-app game-of-life
EXAMPLES := bottle-app flask-app backend-requests game-of-life

# Default example for serve target
EXAMPLE ?= bottle-app
WASM_FILE := $(BUILD_DIR)/$(EXAMPLE).composed.wasm

TARGET_WORLD := fastly:compute/service

VICEROY ?= viceroy

# Generate WASM file paths for all examples
EXAMPLE_WASMS := $(foreach example,$(EXAMPLES),$(BUILD_DIR)/$(example).wasm)

Expand Down Expand Up @@ -57,7 +59,11 @@ serve: $(WASM_FILE)

# Test all examples (requires all WASM files to be built)
test: $(COMPOSED_WASMS)
uv run --extra test pytest
VICEROY=$(VICEROY) uv run --extra test pytest

# Update snapshots for snapshot tests
test-update-snapshots: $(COMPOSED_WASMS)
VICEROY=$(VICEROY) uv run --extra test pytest --snapshot-update

# List available examples
list-examples:
Expand Down Expand Up @@ -93,6 +99,7 @@ help:
@echo " all Build all examples"
@echo " serve [EXAMPLE=name] Serve example (default: $(EXAMPLE))"
@echo " test Run integration tests (builds all examples)"
@echo " test-update-snapshots Update snapshot test baselines"
@echo " build-all Build all examples (alias for 'all')"
@echo " list-examples List available examples"
@echo " clean Clean build artifacts"
Expand All @@ -109,4 +116,4 @@ help:
@echo ""
@echo "Available examples: $(EXAMPLES)"

.PHONY: all serve test list-examples build-all clean lint lint-fix format format-check help $(WASILESS_WASM)
.PHONY: all serve test test-update-snapshots list-examples build-all clean lint lint-fix format format-check help $(WASILESS_WASM)
89 changes: 89 additions & 0 deletions examples/backend-requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Example demonstrating interactions with backends via the requests facade"""

import json
import traceback
import typing
from typing import Any

from bottle import Bottle, request

import fastly_compute.requests as requests
from fastly_compute.wsgi import WsgiHttpIncoming

app = Bottle()


def _map_error(e: Exception):
"""Return a standardized error response.

Args:
e: The exception that occurred
demo: The demo/endpoint name for this error
"""
return {
"error": repr(e),
"error_type": type(e).__name__,
"tb": traceback.format_exc(),
}


def _proxy_request(method: str, url: str, **kwargs) -> dict[str, Any]:
try:
response = requests.request(method, url, **kwargs)
except Exception as e:
return _map_error(e)

return {
"method": method,
"url": url,
"fastly_backend": kwargs.get("fastly_backend"),
"status_code": response.status_code,
"success": response.ok,
"headers_count": len(response.headers),
"content_length": len(response.content),
"response_preview": response.text[:200] + "..."
if len(response.text) > 200
else response.text,
}


@app.route("/proxy/<method>")
def proxy(method: str):
"""Proxy endpoint for testing; pass through parameters.

Don't deploy actual code that does this as it is likely
to be abused if allowed to hit unrestricted domains.
This proxy method is intended for testing use.
"""
# Type checker doesn't quite understand bottle's DictProperty
# so we need to give it hints.
query = typing.cast(typing.Mapping[str, str], request.query)
headers_dict = typing.cast(typing.Mapping[str, str], request.headers)

url: str | None = query.get("url")
if not url:
return {"error": "url query parameter is required"}

backend = query.get("fastly_backend")
json_param = query.get("json")

# Reconstitute JSON parameter if present
json_data = None
if json_param:
try:
json_data = json.loads(json_param)
except json.JSONDecodeError as e:
return {"error": f"Invalid JSON in json parameter: {e}"}

headers = {}
for k, v in headers_dict.items():
if k.lower() != "host":
headers[k] = v

return _proxy_request(
method, url=url, fastly_backend=backend, json=json_data, headers=headers
)


# Create the HTTP handler
HttpIncoming = WsgiHttpIncoming(app)
Loading