pgkiln 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SECURITY.md +2 -2
- package/db/migrations/116_dump_jobs.sql +41 -0
- package/db/migrations/117_rest_handlers.sql +63 -0
- package/db/migrations/118_change_log.sql +54 -0
- package/docs/apex-feature-parity.md +7 -7
- package/docs/guide/01-installation.md +4 -0
- package/docs/guide/03-builder.md +179 -5
- package/docs/guide/08-security.md +11 -0
- package/docs/guide/12-development.md +44 -6
- package/docs/guide/13-rest-api.md +59 -1
- package/docs/guide/15-extensions.md +1 -1
- package/examples/hr/hr_62_rest_module.sql +39 -0
- package/package.json +1 -1
- package/public/app.css +1 -1
- package/public/builder-icons.svg +2 -0
- package/public/builder.css +420 -173
- package/public/builder.js +48 -0
- package/public/sql-monitor.js +73 -0
- package/src/app.ts +6 -0
- package/src/automations.ts +6 -0
- package/src/binds.ts +2 -1
- package/src/builder/advisor.ts +24 -2
- package/src/builder/autorest.ts +3 -2
- package/src/builder/changelog.ts +240 -0
- package/src/builder/components.ts +1 -1
- package/src/builder/datapump.ts +364 -0
- package/src/builder/ddl-tools.ts +13 -4
- package/src/builder/find-workspace.ts +2 -2
- package/src/builder/plan-view.ts +25 -0
- package/src/builder/rest-modules.ts +629 -0
- package/src/builder/rest.ts +1 -0
- package/src/builder/routes.ts +4 -2
- package/src/builder/scheduling.ts +495 -0
- package/src/builder/sql-monitor.ts +397 -0
- package/src/builder/sql-tools.ts +6 -2
- package/src/builder/sql-utilities.ts +7 -39
- package/src/builder/sql.ts +6 -0
- package/src/builder/table-editor.ts +4 -1
- package/src/builder/top-sql.ts +24 -6
- package/src/builder/ui.ts +6 -4
- package/src/builder/worksheet.ts +5 -21
- package/src/changelog.ts +109 -0
- package/src/cron-schedule.ts +282 -0
- package/src/datapump.ts +546 -0
- package/src/dumpscript.ts +407 -0
- package/src/pgcron.ts +225 -0
- package/src/runtime/rest-handlers.ts +75 -0
- package/src/runtime/rest.ts +116 -22
|
@@ -70,6 +70,8 @@ src/
|
|
|
70
70
|
sqlworksheet.ts (sprint 91) the worksheet's runs: pickStatements() (at the cursor, a selection, all), runStatements() (a log per
|
|
71
71
|
statement, psql command tags, notices, error line and column), explainStatement() (EXPLAIN / rolled-back ANALYZE)
|
|
72
72
|
datamodel.ts (sprint 98) Data Modeler diagrams: readModel() (catalog), boxSize(), fkPath() (crow's feet), autoLayout(), diagramSvg()
|
|
73
|
+
changelog.ts (sprint 106, migration 118) the change log: isSchemaDdl(), ddlTarget() (schema and object), changeName()
|
|
74
|
+
(add_bonus, create_index_x), recordChanges() (grouped per schema), Flyway / Liquibase / plain SQL writers
|
|
73
75
|
tabledef.ts (sprint 95) a table as a definition: readTable(), checkTable(), tableSql() (CREATE, or ALTER from the catalog's version)
|
|
74
76
|
sqlformat.ts (sprint 93) formatSql(): the worksheet's Format (clauses per line, keyword case; strings and comments kept)
|
|
75
77
|
sqlplan.ts (sprint 92) EXPLAIN (FORMAT JSON) as a tree: parsePlan() (self time, slowest step, misestimates), planText()
|
|
@@ -87,9 +89,19 @@ src/
|
|
|
87
89
|
mvt.ts Mapbox Vector Tile encoding for map layers served as tiles
|
|
88
90
|
yamltext.ts the text style of the directory export: a strict YAML subset, written and read
|
|
89
91
|
webclient.ts outgoing HTTP to web services: allow-list, address checks at connect time (SSRF), redirects, limits
|
|
92
|
+
datapump.ts (sprint 103, DA-16) Schema Export / Import jobs (meta.dump_job): findTools (pg_dump/pg_restore ≥ the
|
|
93
|
+
server's major), startExport (pg_dump, argument array, PGPASSWORD), readToc/parseToc (pg_restore --list),
|
|
94
|
+
startImport (pg_restore -f - run in one transaction between schema renames; data-only in FK order),
|
|
95
|
+
cancel, delete, dumpTick (interrupted jobs, expired files; called by the scheduler)
|
|
96
|
+
dumpscript.ts (sprint 103) the import's script runner: ScriptLexer (psql-style split, COPY data, \restrict),
|
|
97
|
+
checkStatement (only the dump's schema; no roles, DO, COPY to/from files…), COPY FROM STDIN as a pg submittable
|
|
90
98
|
dbroles.ts (sprint 99, DA-13) Database Users' SQL: roleSql (CREATE/ALTER ROLE, memberships, search path, schema
|
|
91
99
|
and default privileges: only what differs from the catalog), dropSql, checkRole/checkPassword,
|
|
92
100
|
scramVerifier (SCRAM-SHA-256, SASLprep) and the password slot (maskSql for review and log, withVerifier to run)
|
|
101
|
+
cron-schedule.ts (sprint 101, DA-12) pg_cron schedules without a database: parseSchedule (cron fields, $, n seconds, macros),
|
|
102
|
+
nextRuns in a time zone, describeSchedule in words, the builder's presets and fields
|
|
103
|
+
pgcron.ts (sprint 101, DA-12) pg_cron for SQL Workshop → Scheduling: cronState (installed, loaded, cron.database_name,
|
|
104
|
+
access), jobs and runs, jobSql/activeSql/unscheduleSql/purgeSql (reviewed, then applySql), runNow
|
|
93
105
|
secrets.ts secrets at rest (web credentials): AES-256-GCM with PGKILN_SECRET_KEY
|
|
94
106
|
websources.ts web credentials (OAuth2 client credentials/password/refresh token grants, token cache, stored refresh
|
|
95
107
|
tokens) and REST data sources: requests, JSON paths, typed rows, response cache, write-back operations
|
|
@@ -152,7 +164,10 @@ src/
|
|
|
152
164
|
maps.ts map region (data for Leaflet: layers with a query each, markers, clusters or heat, PostGIS geometry as GeoJSON, report filter by area or distance; list fallback, head assets)
|
|
153
165
|
spatial.ts spatial filtering on the server: map area and distance parsing, PostGIS detection and SQL (ST_Intersects, ST_DWithin), lat/lng fallback (bounding box, haversine)
|
|
154
166
|
pwa.ts Progressive Web App: manifest, service worker route, icons (PNG encoder), offline page
|
|
155
|
-
rest.ts REST modules: handler checks, matching, bearer tokens,
|
|
167
|
+
rest.ts REST modules: handler and template checks, matching, bearer tokens, module privilege, the constrained-role
|
|
168
|
+
check, execution (collections stream from a cursor), OpenAPI (templates, documented parameters, roles)
|
|
169
|
+
rest-handlers.ts (sprint 104, migration 117) PL/pgSQL handlers (binds as settings, DO block, meta.rest_reply) and media
|
|
170
|
+
handlers (content type and file name cleaned, sandboxed response)
|
|
156
171
|
autorest.ts (sprint 100, migration 113) AutoREST: /a/:alias/autorest/<object>/ for the tables and views SQL Workshop →
|
|
157
172
|
RESTful Services published (meta.rest_object): caller() of rest.ts, SET ROLE to the app role, owner/BYPASSRLS
|
|
158
173
|
check, columns from the catalog (column grants), filters/q/order/paging, POST/PUT/DELETE by primary key, OpenAPI
|
|
@@ -246,9 +261,14 @@ src/
|
|
|
246
261
|
quicksql.ts SQL Workshop → Quick SQL page (preview, save as script, run)
|
|
247
262
|
querybuilder.ts SQL Workshop → Query Builder: catalog, joins by foreign key or drawn, functions/group by, buildQuery() from the URL; the canvas is in builder.js
|
|
248
263
|
users.ts user directory and identity providers
|
|
264
|
+
datapump.ts (sprint 103, DA-16) SQL Workshop → Schema Export / Import (/builder/sql/data-pump, administrators only):
|
|
265
|
+
jobs, export form, upload (streamed to PGKILN_DUMP_DIR), archive preview and import options, job log, download
|
|
249
266
|
db-users.ts (sprint 99, DA-13) Administration → Database Users (/builder/db-users, administrators only): PostgreSQL
|
|
250
267
|
roles with highlights, create/edit (review the masked SQL, then Apply in one transaction), drop with
|
|
251
268
|
reassigning; pgkiln's own roles read-only, roles of applications and workspaces not dropped
|
|
269
|
+
scheduling.ts (sprint 101, DA-12) SQL Workshop → Scheduling (/builder/sql/scheduling, administrators only): pg_cron jobs
|
|
270
|
+
with next runs, create/edit with a schedule builder and preview (review the SQL, then Apply), disable,
|
|
271
|
+
run now, unschedule, run history and purge; without pg_cron the install note and pgkiln's automations
|
|
252
272
|
api.ts per-app REST API page (API role, tokens)
|
|
253
273
|
globalization.ts translations, XLIFF/CSV, text messages
|
|
254
274
|
dataload.ts SQL Workshop → Load Data (with definitions, save a mapping as one); data load definition spec (Shared Components)
|
|
@@ -260,11 +280,18 @@ src/
|
|
|
260
280
|
region-settings.ts page designer: settings forms for grid, chart (gauge, drill-down), cards, calendar (views, create, drag and drop), facets, smart filters, display selector, list
|
|
261
281
|
search.ts app search, "where used" (appEntries, search, whereUsed, usedInPanel)
|
|
262
282
|
advisor.ts Advisor: EXPLAIN every SQL fragment, reference checks, plpgsql_check
|
|
263
|
-
ddl-tools.ts SQL Workshop → DDL and Compare (src/ddl.ts)
|
|
283
|
+
ddl-tools.ts SQL Workshop → DDL and Compare (src/ddl.ts); (sprint 106) the Apply and record form
|
|
284
|
+
changelog.ts (sprint 106) SQL Workshop → Change Log (/builder/sql/changes): change sets per schema, latest app export,
|
|
285
|
+
downloads (POST, mark exported_at), rename, delete; /sql/compare/apply (one transaction, recorded)
|
|
264
286
|
sql-utilities.ts SQL Workshop → Utilities (sprint 62): the tools' tiles, OBJECT_REPORTS (fixed catalogue SQL over the
|
|
265
|
-
connection's usable schemas), About Database
|
|
287
|
+
connection's usable schemas), About Database; read only, with a timeout; /sql/utilities/monitor redirects to the SQL Monitor
|
|
266
288
|
login-page.ts Application settings → Login page and Forgot password; Create a login page (page 9999)
|
|
267
|
-
top-sql.ts Top SQL per app role from pg_stat_statements
|
|
289
|
+
top-sql.ts Top SQL per app role from pg_stat_statements; (sprint 102) statementsState / statementsNote (installed and loaded?)
|
|
290
|
+
sql-monitor.ts (sprint 102, DA-14) SQL Workshop → SQL Monitor (administrators): /sql/monitor (sessions, wait events, progress;
|
|
291
|
+
?fragment=1 the live part), POST /sql/monitor/session (cancel / terminate), /sql/monitor/top (topStatements:
|
|
292
|
+
pg_stat_statements sortable, bars), /reset, /statements/:queryid (totals by role, explainForMonitor: EXPLAIN only,
|
|
293
|
+
one statement by the extended protocol, read only, generic plan for $1), /statements/:queryid/worksheet
|
|
294
|
+
plan-view.ts (sprint 92; shared since 102) planView(): a plan (src/sqlplan.ts) as a tree, for the worksheet and the SQL Monitor
|
|
268
295
|
diagnostics.ts Activity → Debug messages (level, list, one request's entries, purge); Workspace utilities →
|
|
269
296
|
Installation (version, install/upgrade runs, applied and missing migrations; administrators)
|
|
270
297
|
ldap.ts Users → LDAP directories
|
|
@@ -279,6 +306,9 @@ src/
|
|
|
279
306
|
autorest.ts (sprint 100) SQL Workshop → RESTful Services (/builder/sql/rest): publish a table or view through an
|
|
280
307
|
application of the workspace (checks: schema the workshop may use, app role not the owner), its page
|
|
281
308
|
(grants per method, RLS, security_invoker, endpoints, cURL/fetch, OpenAPI link), settings, disable
|
|
309
|
+
rest-modules.ts (sprint 104) SQL Workshop → RESTful Services → Modules (/builder/sql/rest/modules): modules of the
|
|
310
|
+
workspace's applications, the module › template › handler tree, handler forms, Try it (inject with a
|
|
311
|
+
short-lived token), Run only / lock checks
|
|
282
312
|
workflows.ts workflow versions, diagram and instances (Shared Components)
|
|
283
313
|
process-jobs.ts page designer: the Jobs tab of a background chain process
|
|
284
314
|
template-spec.ts template component property form (Shared Components)
|
|
@@ -311,12 +341,14 @@ public/
|
|
|
311
341
|
app.js client runtime: dialogs (dialog_closed actions), popup LOVs, dynamic actions (focus, classes, messages), grids (add/duplicate rows, master-detail refresh, move/resize columns, copy/paste of cell ranges), menus, lazy regions, maps (Leaflet layers, marker clusters, heat layer, layer legend, area/distance filter) (no inline JS)
|
|
312
342
|
code-editor.js, .css builder code editor: enhances <textarea data-code>, highlighting, suggestions (no dependencies)
|
|
313
343
|
palette.js (sprint 96) the builder's search dialog (Ctrl+K), recently visited SQL Workshop pages, the Launchpad's tabs
|
|
344
|
+
sql-monitor.js (sprint 102) the SQL Monitor: reloads the sessions' live part every 5 / 15 / 60 s, Pause (localStorage pgkiln.sqlMonitor)
|
|
314
345
|
data-modeler.js, .css (sprint 98) the Data Modeler: drag boxes, undo/redo, save, zoom/fit, auto layout, mini-map, SVG/PNG export, print
|
|
315
346
|
worksheet.js, .css (sprint 91) the SQL Worksheet: run keys with the cursor, output tabs, navigator swap and search, drag and drop, splitters;
|
|
316
347
|
(92) grid sort, value dialog, history filter; (93) worksheet tabs in localStorage, Files, binds dialog, format, find, case
|
|
317
|
-
builder.css builder only: IDE look (dark chrome, icon rail, panes)
|
|
348
|
+
builder.css builder only: IDE look (dark chrome, icon rail, panes); design tokens at the top (colour,
|
|
349
|
+
elevation, radius, type, motion; light and dark sets, sprint 105), then the shared controls
|
|
318
350
|
builder.js builder only: tabs, component tree, property filter, drag and drop on the layout
|
|
319
|
-
builder-icons.svg builder only: icons of the rail, toolbar and designer (b-*)
|
|
351
|
+
builder-icons.svg builder only: icons of the rail, toolbar and designer (b-*); b-kiln is the brand mark (brandMark() in ui.ts)
|
|
320
352
|
test/
|
|
321
353
|
binds.test.ts unit tests
|
|
322
354
|
security.test.ts security regression tests (in-process, against the database)
|
|
@@ -333,9 +365,14 @@ test/
|
|
|
333
365
|
unload.test.ts Unload Data: CSV/JSON/XLSX/XML output, read back with Load Data, read-only and one-statement checks, streaming
|
|
334
366
|
app-from-file.test.ts Create → From a file: proposed names and types, app + table + rows + pages, row errors, login, validation
|
|
335
367
|
workshop.test.ts SQL scripts, Quick SQL pages, query builder, data load definitions (Load Data, the process, export)
|
|
368
|
+
cron-schedule.test.ts (sprint 101) pg_cron schedules: parsing and errors, next runs across time zones and daylight saving, words
|
|
369
|
+
scheduling.test.ts (sprint 101) SQL Workshop → Scheduling: the SQL, the no-pg_cron page, create/edit/disable/run now/unschedule,
|
|
370
|
+
history and purge; against pg_cron when the test database has it, else test/pgcron-mock.ts (a stand-in)
|
|
336
371
|
sql-charts.test.ts (sprint 97) the worksheet's Chart tab, saving a chart, the Charts dashboard, a chart's page, read-only runs
|
|
337
372
|
launchpad.test.ts (sprint 96) Launchpad groups, pins (builder pages only, at most 24), the search (JSON and page)
|
|
338
373
|
tabledef.test.ts (sprint 95) table definitions round trip: CREATE, read, ALTER, read again; mistakes
|
|
374
|
+
changelog.test.ts (sprint 106) DDL recognition and names, recording from Table Properties, the worksheet and Compare,
|
|
375
|
+
downloads (Flyway, Liquibase, SQL), stable numbers, the latest application export
|
|
339
376
|
table-editor.test.ts (sprint 95) the Table Properties page: preview, apply, a failing apply rolls back, template, worksheet
|
|
340
377
|
object-actions.test.ts (sprint 94) object menus: Quick DDL, Drop with dependents, Add index / trigger / policy, the panel, not-found
|
|
341
378
|
data-modeler.test.ts (sprint 98) Data Modeler: "hr core" with its FK lines, saved positions, SVG, objects, DDL, dictionary, geometry
|
|
@@ -352,6 +389,7 @@ test/
|
|
|
352
389
|
charts.test.ts chart markup per kind (geometry as classes), gauges, Gantt time axis and dependencies, pyramid, polar, range, box plot, stock, drill-down links
|
|
353
390
|
calendar.test.ts calendar views, create links, moving events (pure and over HTTP)
|
|
354
391
|
rest-sources.test.ts REST data sources, web credentials, SSRF checks, invoke_api (mock service + HR page 23)
|
|
392
|
+
rest-modules.test.ts (sprint 104) modules, templates, PL/pgSQL and media handlers, OpenAPI, the builder's tree and Try it
|
|
355
393
|
workflow-invoke.test.ts workflow invoke_api steps: the call between transactions, faults, retry, lease, Advisor, export (mock service)
|
|
356
394
|
large-tables.test.ts row ranges, max_rows, row limits, lazy regions, region caching, streamed downloads (HR page 25)
|
|
357
395
|
grid.test.ts interactive grid: aggregates, layouts per user, saved grid reports, master-detail, row actions (HR page 27)
|
|
@@ -31,11 +31,12 @@ answer 404.
|
|
|
31
31
|
|---|---|
|
|
32
32
|
| `method` | `GET`, `POST`, `PUT`, `PATCH` or `DELETE` |
|
|
33
33
|
| `path` | Segments with `:parameters`, e.g. `orders/:id/lines` |
|
|
34
|
-
| `type` | `collection`: a SELECT, returned page by page as `{items, offset, limit, has_more}` (`?limit=` up to 500, `?offset=`, `page_size` for the default); the page streams from a cursor as a chunked JSON array (an error before the first rows is still a 4xx/5xx, a failure after them ends the response short) · `item`: one row as an object, 404 when there is none · `sql`: statements; the first row of the last one is the response (201 for POST, 204 without a row; `status` overrides) |
|
|
34
|
+
| `type` | `collection`: a SELECT, returned page by page as `{items, offset, limit, has_more}` (`?limit=` up to 500, `?offset=`, `page_size` for the default); the page streams from a cursor as a chunked JSON array (an error before the first rows is still a 4xx/5xx, a failure after them ends the response short) · `item`: one row as an object, 404 when there is none · `sql`: statements; the first row of the last one is the response (201 for POST, 204 without a row; `status` overrides) · `plpgsql` (sprint 104): a PL/pgSQL block that answers with `meta.rest_reply(body, status)` ([below](#modules-templates-and-handlers-in-the-sql-workshop)) · `media`: a query whose row has `content_type`, `content` and optionally `filename`, sent as that content |
|
|
35
35
|
| `source` | The SQL. Binds: path parameters, query parameters and the fields of a JSON (or form) body, upper case (`:ID`, `:CUSTOMER`); `:BODY` is the whole JSON body. `:APP_USER` (the caller), `:APP_ID` and `:APP_ALIAS` are set by the server: a parameter of that name (or `APP_SESSION`, `APP_PAGE_ID`, `REQUEST`, `APP_LANGUAGE`) is ignored, and can't be a path parameter |
|
|
36
36
|
| `roles` | The caller needs one of these roles (as `meta.has_role()` sees them) |
|
|
37
37
|
| `auth` | `token` (default) or `public` (no token; the SQL runs with `meta.app_user()` = `nobody`) |
|
|
38
38
|
| `description` | For the OpenAPI description |
|
|
39
|
+
| `params` | (sprint 104) Documented parameters: `[{"name": "start_date", "in": "body", "type": "string", "required": true, "description": "…"}]` (`in`: `query` or `body`; types `string`, `integer`, `number`, `boolean`). They go into the OpenAPI document; a required one that is missing or empty answers 400 |
|
|
39
40
|
|
|
40
41
|
**Calling them.** Send `Authorization: Bearer <token>` with a token from **App → REST API**: a token
|
|
41
42
|
issued for an account, or one from `POST /oauth/token` for an OAuth client ([below](#tokens)). An
|
|
@@ -63,6 +64,63 @@ curl -X POST http://127.0.0.1:3100/a/hr/rest/v1/leave -H "Authorization: Bearer
|
|
|
63
64
|
-H "Content-Type: application/json" -d '{"start_date": "2027-05-03", "end_date": "2027-05-04", "reason": "dentist"}'
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
## Modules, templates and handlers in the SQL Workshop
|
|
68
|
+
|
|
69
|
+
**SQL Workshop → RESTful Services → Modules** (sprint 104; Oracle Database Actions / ORDS: REST › Modules) edits
|
|
70
|
+
the same REST modules the ORDS way, for every application of the workspace:
|
|
71
|
+
|
|
72
|
+
- a **module** is a base path, `/a/<alias>/rest/<name>/`, with a title, a description, *Enabled* and its
|
|
73
|
+
**privilege**: application roles (as `meta.has_role()` sees them) of which a caller needs one for *every*
|
|
74
|
+
handler of the module, public ones included;
|
|
75
|
+
- a **template** is a URI below it with `:parameters`, e.g. `emp/:id`, with a description for the OpenAPI document
|
|
76
|
+
(a template can exist before its first handler; renaming it moves its handlers);
|
|
77
|
+
- a **handler** answers one method of a template: its source type (*collection query*, *single row*, *SQL*,
|
|
78
|
+
*PL/pgSQL*, *media*), the source, who may call it (a token, or explicitly *public*), further roles, the page size
|
|
79
|
+
or status, and documented parameters, one per line: `start_date body string required First day`.
|
|
80
|
+
|
|
81
|
+
The tree on the left of each page shows module › templates › handlers. A Run only or locked application refuses
|
|
82
|
+
changes. The module's page checks the role the handlers run as and links the module's **OpenAPI** document.
|
|
83
|
+
|
|
84
|
+
**PL/pgSQL handlers** run their block as a `DO` statement as the application's database role. A `DO` block takes
|
|
85
|
+
no parameters, so pgkiln sets the request's binds as transaction-local settings (with query parameters: values
|
|
86
|
+
never become SQL text) and turns `:NAME` in the block into `meta.rest_param('NAME')` (text, NULL when absent):
|
|
87
|
+
|
|
88
|
+
```sql
|
|
89
|
+
declare
|
|
90
|
+
v_id int;
|
|
91
|
+
begin
|
|
92
|
+
v_id := sales.create_order(:CUSTOMER, :TOTAL::numeric);
|
|
93
|
+
perform meta.rest_reply(jsonb_build_object('id', v_id), 201);
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`meta.rest_reply(body jsonb, status int)` sets the response (status 200–599, also 4xx for your own errors; the
|
|
98
|
+
transaction is still committed); without it the handler answers 204. A `raise exception` answers 400 with its
|
|
99
|
+
message and rolls back. Statements without `begin … end` are wrapped in one.
|
|
100
|
+
|
|
101
|
+
**Media handlers** run a query whose first row has `content_type`, `content` (bytea or text) and optionally
|
|
102
|
+
`filename`; the response is that content, sent with `X-Content-Type-Options: nosniff` and a sandboxing
|
|
103
|
+
Content-Security-Policy, so HTML or SVG from a table can't run script in the application's origin. No row: 404.
|
|
104
|
+
|
|
105
|
+
**Try it.** A handler's page sends a request to the real endpoint, with path parameters, a query string and a JSON
|
|
106
|
+
body, either without a token (to see the 401) or *as* an account of the application with a short-lived token (the
|
|
107
|
+
same as issuing one on App → REST API). A PUT, POST or DELETE changes data. The response's status and body are
|
|
108
|
+
shown in the page; it works without JavaScript.
|
|
109
|
+
|
|
110
|
+
**Example:** the HR example's module `hr` (`examples/hr/hr_62_rest_module.sql`): `GET emp/:id` (single row),
|
|
111
|
+
`GET emp/:id/photo` (media), `POST leave` (PL/pgSQL with body parameters) and `GET payroll/:deptno` (roles manager or
|
|
112
|
+
admin):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
curl http://127.0.0.1:3100/a/hr/rest/hr/emp/7839 -H "Authorization: Bearer $TOKEN"
|
|
116
|
+
curl -X POST http://127.0.0.1:3100/a/hr/rest/hr/leave -H "Authorization: Bearer $TOKEN" \
|
|
117
|
+
-H "Content-Type: application/json" -d '{"start_date": "2027-05-03", "end_date": "2027-05-04", "reason": "dentist"}'
|
|
118
|
+
# {"id": 12, "status": "requested"}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Handlers never run as a superuser, a role that bypasses row level security or pgkiln's owner: such a request
|
|
122
|
+
answers 403 (set `RUNTIME_DATABASE_URL` and give the application its own database role).
|
|
123
|
+
|
|
66
124
|
## AutoREST: tables and views from the SQL Workshop
|
|
67
125
|
|
|
68
126
|
**SQL Workshop → RESTful Services** (`/builder/sql/rest`; Database Actions: a table's *REST › Enable*, ORDS
|
|
@@ -19,7 +19,7 @@ setup`) and on practically every managed service. Install with `create extension
|
|
|
19
19
|
| **unaccent** | Accent-insensitive search (`café` finds `cafe`), essential in multilingual apps | Text search options |
|
|
20
20
|
| **citext** | Case-insensitive text columns (e-mail addresses, codes) without `lower()` everywhere | NLS_COMP=LINGUISTIC |
|
|
21
21
|
| **btree_gist** | Exclusion constraints on ranges, e.g. **no overlapping bookings/leave** enforced by the database, race-free | Custom PL/SQL checks |
|
|
22
|
-
| **pg_stat_statements** | Which SQL is slow or called most, per role, so per app (`db_role`) | Monitor Activity → Top SQL |
|
|
22
|
+
| **pg_stat_statements** | Which SQL is slow or called most, per role, so per app (`db_role`) | Monitor Activity → Top SQL; SQL Workshop → SQL Monitor (Top SQL, a statement's totals) |
|
|
23
23
|
| **tablefunc** | `crosstab()` for pivot tables | Interactive report *Pivot* |
|
|
24
24
|
| **ltree** | Hierarchies (org charts, categories, menus) with fast subtree queries | Tree region, `CONNECT BY` |
|
|
25
25
|
| **postgres_fdw**, **file_fdw** | Query another PostgreSQL database, or a CSV file, as if it were a table | Remote servers / database links |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 62: a REST module built in SQL Workshop › RESTful
|
|
3
|
+
-- Services (sprint 104; docs/guide/13-rest-api.md → Modules, templates
|
|
4
|
+
-- and handlers). Module "hr" at /a/hr/rest/hr/, with a token from
|
|
5
|
+
-- App › REST API:
|
|
6
|
+
--
|
|
7
|
+
-- emp/ GET collection query: the active employees
|
|
8
|
+
-- emp/:id GET single row: one employee
|
|
9
|
+
-- emp/:id/photo GET media: the employee's photo
|
|
10
|
+
-- leave POST PL/pgSQL: request leave with
|
|
11
|
+
-- {"start_date": "…", "end_date": "…", "reason": "…"}
|
|
12
|
+
-- payroll/:deptno GET collection query; managers and administrators only
|
|
13
|
+
-- =====================================================================
|
|
14
|
+
insert into meta.rest_module (app_id, name, title, description, templates, handlers)
|
|
15
|
+
select id, 'hr', 'HR (RESTful Services)', 'Employees and leave requests: a module of templates and handlers.',
|
|
16
|
+
$t$[
|
|
17
|
+
{"uri": "emp", "description": "The employees"},
|
|
18
|
+
{"uri": "emp/:id", "description": "One employee by number"},
|
|
19
|
+
{"uri": "emp/:id/photo", "description": "An employee's photo"},
|
|
20
|
+
{"uri": "leave", "description": "Leave requests of the caller"},
|
|
21
|
+
{"uri": "payroll/:deptno", "description": "Payroll of a department"}
|
|
22
|
+
]$t$::jsonb,
|
|
23
|
+
$h$[
|
|
24
|
+
{"method": "GET", "path": "emp", "type": "collection", "description": "The active employees",
|
|
25
|
+
"source": "select empno, initcap(ename) as name, initcap(job) as job, deptno from hr.emp where active order by empno"},
|
|
26
|
+
{"method": "GET", "path": "emp/:id", "type": "item", "description": "One employee",
|
|
27
|
+
"source": "select empno, initcap(ename) as name, initcap(job) as job, deptno, hiredate, mgr from hr.emp where empno = :ID::int"},
|
|
28
|
+
{"method": "GET", "path": "emp/:id/photo", "type": "media", "description": "The employee's photo",
|
|
29
|
+
"source": "select photo_mime as content_type, photo as content, photo_name as filename from hr.emp where empno = :ID::int and photo is not null"},
|
|
30
|
+
{"method": "POST", "path": "leave", "type": "plpgsql", "description": "Request leave (as the caller)",
|
|
31
|
+
"params": [{"name": "start_date", "in": "body", "type": "string", "required": true, "description": "First day (YYYY-MM-DD)"},
|
|
32
|
+
{"name": "end_date", "in": "body", "type": "string", "required": true, "description": "Last day (YYYY-MM-DD)"},
|
|
33
|
+
{"name": "reason", "in": "body", "type": "string"}],
|
|
34
|
+
"source": "declare\n v_id int;\nbegin\n v_id := hr.request_leave(:START_DATE::date, :END_DATE::date, :REASON);\n perform meta.rest_reply(jsonb_build_object('id', v_id, 'status', 'requested'), 201);\nend"},
|
|
35
|
+
{"method": "GET", "path": "payroll/:deptno", "type": "collection", "roles": ["manager", "admin"], "description": "Salaries of a department",
|
|
36
|
+
"source": "select empno, initcap(ename) as name, sal from hr.emp where deptno = :DEPTNO::int and active order by empno"}
|
|
37
|
+
]$h$::jsonb
|
|
38
|
+
from meta.app where alias = 'hr'
|
|
39
|
+
on conflict (app_id, name) do nothing;
|
package/package.json
CHANGED
package/public/app.css
CHANGED
|
@@ -416,7 +416,7 @@ dialog.region-inline_drawer:target { inset: 0 0 0 auto; }
|
|
|
416
416
|
.req { color: var(--danger); margin-left: 0.2em; }
|
|
417
417
|
input:not([type=checkbox]):not([type=radio]), select, textarea {
|
|
418
418
|
font: inherit; color: var(--text); background: var(--surface);
|
|
419
|
-
border: 1px solid var(--border); border-radius: calc(var(--radius) * 0.75); padding: 0.45rem 0.6rem; width: 100%; min-width: 0;
|
|
419
|
+
border: 1px solid var(--field-border, var(--border)); border-radius: calc(var(--radius) * 0.75); padding: 0.45rem 0.6rem; width: 100%; min-width: 0;
|
|
420
420
|
}
|
|
421
421
|
input:disabled, select:disabled, textarea:disabled { opacity: 0.6; }
|
|
422
422
|
textarea.code { font-family: var(--mono); font-size: 0.84rem; line-height: 1.45; tab-size: 2; }
|
package/public/builder-icons.svg
CHANGED
|
@@ -30,4 +30,6 @@
|
|
|
30
30
|
<symbol id="b-save" viewBox="0 0 24 24"><path d="M5 4h11l3 3v13H5z"/><path d="M8 4v5h7V4M8 20v-6h8v6"/></symbol>
|
|
31
31
|
<symbol id="b-dots" viewBox="0 0 24 24"><path d="M5 12h.01M12 12h.01M19 12h.01" stroke-width="3.2"/></symbol>
|
|
32
32
|
<symbol id="b-pages" viewBox="0 0 24 24"><path d="M8 3h8l4 4v11H8z"/><path d="M16 3v4h4"/><path d="M4 7v14h11"/></symbol>
|
|
33
|
+
<!-- (sprint 105) the pgkiln brand mark: an ember flame with a hollow core; filled, unlike the stroke icons -->
|
|
34
|
+
<symbol id="b-kiln" viewBox="0 0 24 24"><path fill="currentColor" stroke="none" fill-rule="evenodd" d="M12.3 1.8c.6 3.1 2.5 4.7 4.2 6.6 1.8 1.9 3.1 4 3.1 6.7a7.6 7.6 0 0 1-15.2 0c0-2.6 1.1-4.6 2.8-6.1.2 1.6 1 2.8 2.2 3.4C9.2 8.6 10.5 4.7 12.3 1.8zM12 20.4a2.9 2.9 0 0 0 2.9-2.9c0-1.4-.5-2.9-1.4-4-.8.8-1.3 1.8-1.4 3-.7-.3-1.1-.9-1.3-1.6-.9.9-1.8 1.9-1.8 3.3a3 3 0 0 0 3 2.2z"/></symbol>
|
|
33
35
|
</svg>
|