ExamplesΒΆ

  1from urllib import request
  2from goblet import Goblet, jsonify, Response, goblet_entrypoint
  3from goblet.alerts.alert_conditions import (
  4    MetricCondition,
  5    LogMatchCondition,
  6    CustomMetricCondition,
  7    UptimeCondition, 
  8    PubSubDLQCondition
  9)
 10from goblet.alerts.alerts import BackendAlert, UptimeAlert, PubSubDLQAlert
 11from goblet.handlers.routes import CORSConfig
 12import asyncio
 13import logging
 14
 15app = Goblet(function_name="goblet-example")
 16app.log.setLevel(logging.INFO)  # configure goblet logger level
 17goblet_entrypoint(app)
 18
 19from typing import List
 20from marshmallow import Schema, fields
 21
 22# Example http trigger
 23@app.http()
 24def main_http(request):
 25    return jsonify(request.json)
 26
 27
 28# Example http trigger that contains header
 29@app.http(headers={"X-Github-Event"})
 30def main_header(request):
 31    return jsonify(request.json)
 32
 33
 34# Example http triggers that matches header
 35@app.http(headers={"X-Github-Event": "issue"})
 36def main(request):
 37    return jsonify(request.json)
 38
 39
 40# Path param
 41@app.route("/home/{test}")
 42def home(test):
 43    return jsonify(test)
 44
 45
 46# Example query args
 47@app.route("/home")
 48def query_args():
 49    request = app.current_request
 50    q = request.args.get("q")
 51    return jsonify(q)
 52
 53
 54# POST request
 55@app.route("/home", methods=["POST"])
 56def post():
 57    request = app.current_request
 58    return jsonify(request.json)
 59
 60
 61# Typed Path Param
 62@app.route("/home/{name}/{id}", methods=["GET"])
 63def namer(name: str, id: int):
 64    return f"{name}: {id}"
 65
 66
 67class Point(Schema):
 68    lat = fields.Int()
 69    lng = fields.Int()
 70
 71
 72# Custom schema types
 73@app.route("/points")
 74def points() -> List[Point]:
 75    point = Point().load({"lat": 0, "lng": 0})
 76    return [point]
 77
 78
 79# Custom Marshmallow Fields
 80from marshmallow_enum import EnumField
 81from enum import Enum
 82
 83
 84def enum_to_properties(self, field, **kwargs):
 85    """
 86    Add an OpenAPI extension for marshmallow_enum.EnumField instances
 87    """
 88    if isinstance(field, EnumField):
 89        return {"type": "string", "enum": [m.name for m in field.enum]}
 90    return {}
 91
 92
 93app.handlers["route"].marshmallow_attribute_function = enum_to_properties
 94
 95
 96class StopLight(Enum):
 97    green = 1
 98    yellow = 2
 99    red = 3
100
101
102class TrafficStop(Schema):
103    light_color = EnumField(StopLight)
104
105
106@app.route("/traffic")
107def traffic() -> TrafficStop:
108    return TrafficStop().dump({"light_color": StopLight.green})
109
110
111# Returns follow openapi spec
112# definitions:
113#   TrafficStop:
114#     type: object
115#     properties:
116#       light_color:
117#         type: string
118#         enum:
119#         - green
120#         - yellow
121#         - red
122
123# Enum paramter 
124@app.route("/{color}")
125def prim_enum(color: StopLight):
126    return StopLight(color)
127
128# Pydantic Typing
129from pydantic import BaseModel
130
131
132class NestedModel(BaseModel):
133    text: str
134
135
136class PydanticModel(BaseModel):
137    id: int
138    nested: NestedModel
139
140
141# Request Body Typing
142@app.route("/pydantic", request_body=PydanticModel)
143def traffic() -> PydanticModel:
144    return jsonify(PydanticModel().dict)
145
146
147# Custom Backend
148@app.route("/custom_backend", backend="https://www.CLOUDRUN_URL.com/home")
149def custom_backend():
150    return
151
152
153# Method Security
154@app.route("/method_security", security=[{"your_custom_auth_id": []}])
155def method_security():
156    return
157
158
159# Custom responses and request_types
160@app.route(
161    "/custom",
162    request_body={
163        "application/json": {"schema": {"type": "array", "items": {"type": "string"}}}
164    },
165    responses={"400": {"description": "400"}},
166)
167def custom():
168    request = app.current_request
169    assert request.data["string1", "string2"]
170    return
171
172
173# Example response object
174@app.route("/response")
175def response():
176    return Response(
177        {"failed": 400}, headers={"Content-Type": "application/json"}, status_code=400
178    )
179
180
181# Example CORS
182
183@app.route('/custom_cors', cors=CORSConfig(allow_origin='localhost', allow_methods=["GET"], extra_headers={"X-TEST":"X-HEADER-VALUE"}))
184def custom_cors():
185    return jsonify('localhost is allowed with GET method')
186
187# Scheduled job
188@app.schedule("5 * * * *")
189def scheduled_job():
190    return jsonify("success")
191
192
193# Scheduled job with custom headers, method, and body
194@app.schedule(
195    "5 * * * *",
196    httpMethod="POST",
197    headers={"X-Custom": "header"},
198    body="BASE64 ENCODED STRING",
199)
200def scheduled_job():
201    headers = app.current_request.headers
202    body = app.current_request.body
203    method = app.current_request.method
204    return jsonify("success")
205
206
207# Pubsub Subscription
208@app.pubsub_subscription("test")
209def test_subscription(data):
210    app.log.info(data)
211    return
212
213
214# Pubsub topic with matching message attributes
215@app.pubsub_subscription("test", attributes={"key": "value"})
216def pubsub_attributes(data):
217    app.log.info(data)
218    return
219
220
221# create a pubsub subscription instead of pubsub triggered function
222@app.pubsub_subscription("test", use_subscription=True)
223def pubsub_subscription_use_subscription(data):
224    return
225
226
227# create a pubsub subscription instead of pubsub triggered function and add filter
228@app.pubsub_subscription(
229    "test",
230    use_subscription=True,
231    filter='attributes.name = "com" AND -attributes:"iana.org/language_tag"',
232)
233def pubsub_subscription_filter(data):
234    return
235
236# Pubsub Subscription with DLQ and alert
237# Triggered by pubsub topic. Simulates failure to trigger DLQ
238@app.pubsub_subscription(
239    "goblet-created-test-topic",
240    dlq=True,
241    dlq_alerts=[
242        PubSubDLQAlert(
243            "pubsubdlq",
244            conditions=[
245                PubSubDLQCondition(
246                    "pubsublq-condition"
247                )
248            ],
249        )
250    ]
251)
252def failed_subscription(data):
253    raise Exception("Simulating failure")
254
255# Create a pubsub topic
256app.pubsub_topic(
257    "test-topic"
258)
259
260# Example Storage trigger on the create/finalize event
261@app.storage("BUCKET_NAME", "finalize")
262def storage(event):
263    app.log.info(event)
264    return
265
266
267# Example before request
268@app.before_request()
269def add_db(request):
270    app.g.db = "db"
271    return request
272
273
274# Example after request
275@app.after_request()
276def add_header(response):
277    response.headers["X-Custom"] = "custom header"
278    return response
279
280
281# Example eventarc pubsub topic
282@app.eventarc(topic="test")
283def pubsub(data):
284    app.log.info("pubsub")
285    return
286
287
288# Example eventarc direct event
289@app.eventarc(
290    event_filters=[
291        {"attribute": "type", "value": "google.cloud.storage.object.v1.finalized"},
292        {"attribute": "bucket", "value": "BUCKET"},
293    ],
294    region="us-east1",
295)
296def bucket(data):
297    app.log.info("bucket_post")
298    return
299
300
301# Example eventarc audit log
302@app.eventarc(
303    event_filters=[
304        {"attribute": "type", "value": "google.cloud.audit.log.v1.written"},
305        {"attribute": "methodName", "value": "storage.objects.get"},
306        {"attribute": "serviceName", "value": "storage.googleapis.com"},
307    ],
308    region="us-central1",
309)
310def bucket_get(data):
311    app.log.info("bucket_get")
312    return
313
314
315# Example Cloudrun Job with schedule
316@app.job("job1", schedule="* * * * *")
317def job1_task1(id):
318    app.log.info(f"job...{id}")
319    return "200"
320
321
322# Example Cloudrun Job with additional task
323@app.job("job1", task_id=1)
324def job1_task2(id):
325    app.log.info(f"different task for job...{id}")
326    return "200"
327
328
329# Example BQ Remote Function
330# Called in BQ with the following sql: SELECT `PROJECT.DATASET.math_example_multiply(x,y,z)` from DATASET.table
331@app.bqremotefunction(dataset_id="DATASET")
332def multiply(x: int, y: int, z: int) -> int:
333    w = x * y * z
334    return w
335
336# Totally contrived example of an async function (a real one would use aiohttp or similar)
337async def async_multiply(x: int, y: int) -> int:
338    w = x * y
339    return w
340
341# Example BQ Remote Function with vectorized function
342# For network-bound BQ Remote Functions, this approach using async will yield significantly better performance
343@app.bqremotefunction(dataset_id="blogs",vectorize_func=True)
344def function_test(x: List[int], y: List[int]) -> List[int]:
345     results = [asyncio.run(async_multiply(elem_x, elem_y)) for elem_x, elem_y in zip(x, y)]
346     return results
347
348
349# Example Redis Instance
350app.redis("redis-test")
351
352# Example VPC Connector
353app.vpcconnector("vpc-conn-test")
354
355# Example Metric Alert for the cloudfunction metric execution_count with a threshold of 10
356metric_alert = BackendAlert(
357    "metric",
358    conditions=[
359        MetricCondition(
360            "test",
361            metric="cloudfunctions.googleapis.com/function/execution_count",
362            value=10
363        )
364    ],
365)
366app.alert(metric_alert)
367
368# Example Metric Alert for the cloudfunction metric execution_times with a custom aggregation
369metric_alert_2 = BackendAlert(
370    "metric",
371    conditions=[
372        MetricCondition(
373            "test",
374            metric="cloudfunctions.googleapis.com/function/execution_times",
375            value=1000,
376            aggregations=[
377                {
378                    "alignmentPeriod": "300s",
379                    "crossSeriesReducer": "REDUCE_NONE",
380                    "perSeriesAligner": "ALIGN_PERCENTILE_50",
381                }
382            ],
383        )
384    ],
385)
386app.alert(metric_alert_2)
387
388# Example Log Match metric that will trigger an incendent off of any Error logs
389log_alert = BackendAlert(
390    "error",
391    conditions=[LogMatchCondition("error", "severity>=ERROR")],
392)
393app.alert(log_alert)
394
395# Example Metric Alert that creates a custom metric for severe errors with http code in the 500's and creates an alert with a threshold of 10
396custom_alert = BackendAlert(
397    "custom",
398    conditions=[
399        CustomMetricCondition(
400            "custom",
401            metric_filter="severity=(ERROR OR CRITICAL OR ALERT OR EMERGENCY) httpRequest.status=(500 OR 501 OR 502 OR 503 OR 504)",
402            value=10,
403        )
404    ],
405)
406app.alert(custom_alert)
407
408# Example CloudTask Queue + CloudTask HTTP Target
409client = app.cloudtaskqueue("queue", config={
410    "rateLimits": {
411        "maxDispatchesPerSecond": 500,
412        "maxBurstSize": 100,
413        "maxConcurrentDispatches": 1000
414    },
415    "retryConfig": {
416        "maxAttempts": 10,
417        "minBackoff": "0.100s",
418        "maxBackoff": "3600s",
419        "maxDoublings": 16
420    }
421})
422
423# Cloudtask HTTP Target
424@app.cloudtasktarget(name="target")
425def my_target_handler(request):
426    ''' handle request '''
427    return {}
428
429# Enqueue a message using the CloudTask Queue client
430@app.route("/enqueue", methods=["GET"])
431def enqueue():
432    payload = {"message": {"title": "enqueue"}}
433    client.enqueue(target="target", payload=payload)
434    return {}
435
436# Example of handling the GobletRouteNotFoundError with a custom response
437@app.errorhandler("GobletRouteNotFoundError")
438def handle_missing_route(error):
439    return Response("Custom Error", status_code=404)
440
441# Example of handling ValueError.
442@app.errorhandler("ValueError")
443def return_error_string(error):
444    return Response(str(error), status_code=200)
445
446# Example uptime check
447@app.uptime(timeout="30s")
448def uptime_check():
449    return "success"
450
451# Example uptime check with alert
452@app.uptime(timeout="30s",alerts=[UptimeAlert("uptime", conditions=[UptimeCondition("uptime")])])
453def uptime_check_with_alert():
454    app.log.info("success")
455    return "success"