- invoke.go: forward sub-path and query string to function pods - server.js v0.1.2: add _path, _query, _method to event - server.py v0.1.1: add _path, _query, _method to event - upload.go: bump runtime versions (nodejs20:v0.1.2, python3.11:v0.1.1) - examples/notes-python: CRUD notes via sub-path routing - sql-runner: generic SQL executor for DDL jobs - notes: CRUD router (/add, /update, /delete) - notes-list: SELECT all notes - init.tf: create TABLE + INDEX on apply
75 lines
2.6 KiB
Python
75 lines
2.6 KiB
Python
# 2026-03-09
|
|
# handler.py — CRUD роутер для таблицы notes.
|
|
# Роутинг по event._path (sub-path URL):
|
|
# POST /fn/default/notes/add?title=...&body=... → INSERT
|
|
# POST /fn/default/notes/update?id=1&title=... → UPDATE
|
|
# POST /fn/default/notes/delete?id=1 → DELETE
|
|
# _path и _query добавляет runtime из HTTP запроса (server.py).
|
|
import os
|
|
import psycopg2
|
|
import psycopg2.extras
|
|
|
|
|
|
def handle(event):
|
|
dsn = os.environ['PG_DSN']
|
|
# sub-path без ведущего слэша: "add", "update", "delete"
|
|
action = event.get('_path', '/').strip('/')
|
|
q = event.get('_query', {})
|
|
|
|
conn = psycopg2.connect(dsn)
|
|
try:
|
|
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
|
|
if action == 'add':
|
|
title = q.get('title') or event.get('title', '')
|
|
body = q.get('body') or event.get('body', '')
|
|
if not title:
|
|
return {'error': 'title is required'}
|
|
cur.execute(
|
|
"INSERT INTO notes (title, body) VALUES (%s, %s)"
|
|
" RETURNING id, title, body, created_at::text",
|
|
(title, body)
|
|
)
|
|
row = cur.fetchone()
|
|
conn.commit()
|
|
return dict(row)
|
|
|
|
elif action == 'update':
|
|
id_ = q.get('id') or event.get('id')
|
|
if not id_:
|
|
return {'error': 'id is required'}
|
|
title = q.get('title') or event.get('title', '')
|
|
body = q.get('body') or event.get('body', '')
|
|
cur.execute(
|
|
"UPDATE notes SET title=%s, body=%s WHERE id=%s"
|
|
" RETURNING id, title, body, created_at::text",
|
|
(title, body, int(id_))
|
|
)
|
|
row = cur.fetchone()
|
|
conn.commit()
|
|
return dict(row) if row else {'error': 'not found'}
|
|
|
|
elif action == 'delete':
|
|
id_ = q.get('id') or event.get('id')
|
|
if not id_:
|
|
return {'error': 'id is required'}
|
|
cur.execute(
|
|
"DELETE FROM notes WHERE id=%s RETURNING id",
|
|
(int(id_),)
|
|
)
|
|
row = cur.fetchone()
|
|
conn.commit()
|
|
return {'deleted': row['id']} if row else {'error': 'not found'}
|
|
|
|
else:
|
|
return {
|
|
'error': f'unknown action: /{action}',
|
|
'hint': 'use /add?title=...&body=..., /update?id=X&title=...&body=..., /delete?id=X'
|
|
}
|
|
|
|
except Exception as e:
|
|
conn.rollback()
|
|
return {'error': str(e)}
|
|
finally:
|
|
conn.close()
|