pgkiln 0.51.0 → 0.52.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 +5 -0
- package/db/migrations/121_ai_agents.sql +94 -0
- package/db/migrations/122_team_development.sql +186 -0
- package/db/migrations/123_workflow_outcomes.sql +407 -0
- package/db/migrations/124_component_details.sql +51 -0
- package/docs/apex-feature-parity.md +12 -10
- package/docs/guide/01-installation.md +7 -3
- package/docs/guide/03-builder.md +121 -7
- package/docs/guide/04-pages-and-regions.md +71 -21
- package/docs/guide/05-items.md +7 -1
- package/docs/guide/06-processing.md +123 -8
- package/docs/guide/07-dynamic-actions.md +34 -0
- package/docs/guide/09-reference.md +2 -1
- package/docs/guide/11-from-apex.md +2 -1
- package/docs/guide/12-development.md +44 -8
- package/docs/guide/20-ai-agents.md +4 -2
- package/examples/hr/README.md +2 -0
- package/examples/hr/hr_64_ai_agent.sql +97 -0
- package/examples/hr/hr_65_ask_reports.sql +21 -0
- package/examples/hr/hr_66_team_development.sql +58 -0
- package/examples/hr/hr_67_workflow_outcomes.sql +113 -0
- package/examples/hr/hr_68_component_details.sql +96 -0
- package/package.json +1 -1
- package/public/app.css +40 -0
- package/public/app.js +134 -20
- package/public/builder.css +83 -1
- package/public/builder.js +47 -0
- package/src/ai/chat.ts +18 -18
- package/src/ai/service.ts +35 -11
- package/src/ai/types.ts +7 -2
- package/src/app.ts +6 -0
- package/src/builder/advisor.ts +6 -3
- package/src/builder/ai-agents.ts +372 -0
- package/src/builder/ai-builder.ts +3 -3
- package/src/builder/ai.ts +13 -9
- package/src/builder/assistant.ts +22 -7
- package/src/builder/components.ts +38 -9
- package/src/builder/designer.ts +8 -3
- package/src/builder/feedback.ts +6 -1
- package/src/builder/locks.ts +7 -1
- package/src/builder/region-help.ts +2 -2
- package/src/builder/routes.ts +4 -1
- package/src/builder/shared.ts +1 -1
- package/src/builder/team.ts +991 -0
- package/src/builder/ui.ts +2 -1
- package/src/builder/workflow-migrate.ts +210 -0
- package/src/builder/workflows.ts +29 -6
- package/src/builder/workspace-home.ts +2 -0
- package/src/builder/workspace-transfer.ts +1 -1
- package/src/cli/replace.ts +7 -1
- package/src/i18n/ar.ts +17 -0
- package/src/i18n/cs.ts +17 -0
- package/src/i18n/da.ts +17 -0
- package/src/i18n/de.ts +17 -0
- package/src/i18n/el.ts +17 -0
- package/src/i18n/es.ts +17 -0
- package/src/i18n/fi.ts +17 -0
- package/src/i18n/fr.ts +17 -0
- package/src/i18n/he.ts +17 -0
- package/src/i18n/it.ts +17 -0
- package/src/i18n/ja.ts +17 -0
- package/src/i18n/ko.ts +17 -0
- package/src/i18n/nb.ts +17 -0
- package/src/i18n/pl.ts +17 -0
- package/src/i18n/pt.ts +17 -0
- package/src/i18n/ru.ts +17 -0
- package/src/i18n/sv.ts +17 -0
- package/src/i18n/tr.ts +17 -0
- package/src/i18n/uk.ts +17 -0
- package/src/i18n/zh.ts +17 -0
- package/src/i18n.ts +34 -0
- package/src/instance.ts +2 -0
- package/src/metadata.ts +9 -3
- package/src/runtime/ai-agents.ts +365 -0
- package/src/runtime/ai-filter.ts +385 -107
- package/src/runtime/assistant.ts +64 -11
- package/src/runtime/context.ts +2 -0
- package/src/runtime/ir-phrases.ts +812 -0
- package/src/runtime/ir-plan.ts +449 -0
- package/src/runtime/items.ts +1 -1
- package/src/runtime/regions.ts +6 -2
- package/src/runtime/render.ts +1 -1
- package/src/runtime/report.ts +1 -1
- package/src/runtime/tasks.ts +63 -12
- package/src/task-events.ts +232 -0
- package/src/team-samples.ts +74 -0
- package/src/team.ts +342 -0
- package/src/workflow.ts +135 -18
- package/src/workspace-export.ts +16 -1
package/SECURITY.md
CHANGED
|
@@ -99,6 +99,11 @@ first version (MVP). Every finding below has a regression test in
|
|
|
99
99
|
| SQL Worksheet transactions (sprint 107) | A worksheet's open transaction is held in server memory under the caller's builder session id plus the tab key and checked against the workspace: every lookup starts from the caller's own session, so another developer or another sign-in never reaches it. Its connection is a plain client built from the workspace's developer login (or the *Run as* application's), never returned to a pool, and closed at the end, so `SET ROLE` reaches only roles that login has and nothing leaks into the builder's pools; *Run as* can't change while it is open. Rolled back on idle timeout, sign-out, session expiry, workspace switch, tab close and server stop, with `idle_in_transaction_session_timeout` as a backstop; at most `WORKSHEET_TXN_MAX_PER_DEVELOPER` per developer and `WORKSHEET_TXN_MAX` per server, each one database connection outside the pools. CSRF and a developer sign-in on every endpoint. |
|
|
100
100
|
| Release check (sprint 109) | Off by default (`PGKILN_RELEASE_CHECK`, or Instance settings for administrators). When on, the server sends one GET to GitHub's releases API at most once a day per process, with a 5-second timeout and a 1 MB limit, and sends nothing about the installation; only a semver version and an https link are kept from the answer, and they are escaped on the Workspace home. Pages never wait for it. |
|
|
101
101
|
| Data Reporter datasets and reporting applications (sprint 110) | Datasets are managed by the workspace's developers with CSRF on every POST; their schema must be one the workspace's SQL Workshop connection may use (never pgkiln's own or the system's), and tables, columns, joins and filters are checked against the catalog. The runtime builds a dataset's SELECT with quoted identifiers, literal values and whitelisted operators, and runs reports as the application's role. A reporting application's role gets SELECT only on the datasets' tables and views, so RLS applies; viewers never reach the report editor (report parameters in the URL are ignored), and only `admin_authz` users unpublish or delete others' reports. Dataset files are checked like a saved dataset before import. |
|
|
102
|
+
| AI Agents and tools (sprint 111) | SQL tools are read-only (functions that change data and data-modifying `WITH` fail) and run as the application's role, so row level security applies; the model can't set `:APP_USER`. A Navigate tool links only to pages the user may open, with item values of that page and a checksum pgkiln builds. An agent's authorization scheme refuses other users before any AI call. Tool arguments and model output reach the page escaped. AI service keys stay on the server: not in builder pages, the tool call log, an export or the runtime role's reach. The builder's agent and tool forms need a developer and the CSRF token. |
|
|
103
|
+
| AI interactive reports (sprint 112) | "Ask in your own words" (the phrase parser and the AI answer) only sets the report's own settings over its shown columns, never a hidden one; operators, functions, colours, chart types and computed-column expressions come from whitelists and are checked; values are query parameters or escaped literals. Chips are edited and removed through a CSRF-checked form whose fields are checked again; a report without `ai_filter`, a region of another page and signed-out users are refused. |
|
|
104
|
+
| Team Development (sprint 113) | Issues, labels, milestones, templates and saved views belong to the developer's current workspace only, and can be neither read nor changed from another; assignees and @mentions are the workspace's developers, links go only to its applications. Every POST needs the session's CSRF token; titles, bodies, comments and names are escaped, Markdown is made safe, label colours come from a palette. Application users and signed-out visitors can't reach it. |
|
|
105
|
+
| Workflow 26.2 (sprint 114) | Migrating running instances needs a developer of the application and the CSRF token; unmapped steps and other applications' workflows are refused. A task ends only with an outcome of its definition. Task event SQL runs as the application's role (after the commit, or in the request for *fail* events). Draft versions can't be activated in the builder, with SQL or into the active steps. The diagram export is for developers only, served as a sandboxed attachment with escaped labels. |
|
|
106
|
+
| Component details (sprint 115) | The popup LOV search needs the CSRF token, caps the page size and page number and binds the search term. A wait popup's message is page data shown as text. A `menu_entry` dynamic action with an authorization the user lacks is neither sent to the page nor run (403); menu entries the user may not use are left out, labels are escaped, and the database refuses crafted action names. |
|
|
102
107
|
|
|
103
108
|
## Findings of the MVP review and their fixes
|
|
104
109
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 121: AI agents and AI tools (sprint 111, WS-4; APEX 26.1 / 26.2: Shared
|
|
3
|
+
-- Components → AI Agents, AI Tools), more AI providers, and a log of
|
|
4
|
+
-- every tool call
|
|
5
|
+
--
|
|
6
|
+
-- meta.app.ai_tools: the application's AI tools, a JSON list. A tool is
|
|
7
|
+
-- what an agent may do, each one an approved, constrained action:
|
|
8
|
+
-- {"name": "top_earners", "type": "sql", "description": "…",
|
|
9
|
+
-- "sql": "select … where dname ilike :DNAME", "parameters": {"DNAME": {"type": "string"}},
|
|
10
|
+
-- "max_rows": 20, "timeout_s": 10, "authz": "MANAGER"}
|
|
11
|
+
-- Types: sql (a query, run as the application's role through RLS in a
|
|
12
|
+
-- read-only transaction with a time limit), function (a PL/pgSQL
|
|
13
|
+
-- function called with named arguments, as the application's role; it
|
|
14
|
+
-- may change data), rest (a REST data source of the application),
|
|
15
|
+
-- javascript (a function registered with pgkiln.actions.register and/or
|
|
16
|
+
-- the dynamic actions of the custom event with the tool's action name,
|
|
17
|
+
-- run in the user's browser after the answer) and navigate (the model
|
|
18
|
+
-- picks one of the tool's pages and item values; pgkiln checks the user
|
|
19
|
+
-- may open the page and builds the URL with its checksum).
|
|
20
|
+
-- meta.app.ai_agents: the application's AI agents, a JSON list:
|
|
21
|
+
-- {"name": "HR_AGENT", "description": "…", "service": "HR_ASSISTANT",
|
|
22
|
+
-- "system": "…", "effort": "low" | "medium" | "high" | null,
|
|
23
|
+
-- "tools": ["top_earners", "open_employee"], "max_steps": 5,
|
|
24
|
+
-- "max_tokens": 2000, "authz": "MANAGER"}
|
|
25
|
+
-- An AI assistant region names an agent ("agent" in its config) instead
|
|
26
|
+
-- of a service, a system prompt and tools of its own.
|
|
27
|
+
-- Both are definitions of the application: export_app copies every column
|
|
28
|
+
-- of meta.app, so they travel with it (no redefinition of export_app /
|
|
29
|
+
-- import_app). An export made before 121 has neither key: the trigger
|
|
30
|
+
-- below turns the NULL from jsonb_populate_record into an empty list.
|
|
31
|
+
-- The builder checks both lists (src/runtime/ai-agents.ts); the runtime
|
|
32
|
+
-- checks them again before it uses them.
|
|
33
|
+
--
|
|
34
|
+
-- meta.ai_service.provider: also "gemini" (Google Gemini through its
|
|
35
|
+
-- OpenAI-compatible endpoint) and "openai_compatible" (any endpoint that
|
|
36
|
+
-- speaks OpenAI's Chat Completions: Ollama, vLLM, Azure OpenAI, a
|
|
37
|
+
-- gateway). An OpenAI-compatible service needs a base URL; its key may
|
|
38
|
+
-- be empty (a local Ollama needs none).
|
|
39
|
+
--
|
|
40
|
+
-- meta.ai_tool_call: one row per tool call of an assistant or agent (app,
|
|
41
|
+
-- page, user, agent, tool, type, the arguments the model gave, status,
|
|
42
|
+
-- duration, a short message). The arguments may hold user data: only
|
|
43
|
+
-- the owner reads the table (Activity → AI usage); kept 30 days.
|
|
44
|
+
-- Installation data, like meta.ai_usage: not exported.
|
|
45
|
+
-- =====================================================================
|
|
46
|
+
|
|
47
|
+
alter table meta.app add column ai_tools jsonb not null default '[]'
|
|
48
|
+
check (jsonb_typeof(ai_tools) = 'array');
|
|
49
|
+
alter table meta.app add column ai_agents jsonb not null default '[]'
|
|
50
|
+
check (jsonb_typeof(ai_agents) = 'array');
|
|
51
|
+
comment on column meta.app.ai_tools is 'AI tools (121): what the application''s AI agents may do (sql, function, rest, javascript, navigate)';
|
|
52
|
+
comment on column meta.app.ai_agents is 'AI agents (121): AI service, system prompt, reasoning effort, tools, limits and authorization of each agent';
|
|
53
|
+
|
|
54
|
+
create function meta.app_ai_default() returns trigger
|
|
55
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
56
|
+
begin
|
|
57
|
+
new.ai_tools := coalesce(new.ai_tools, '[]');
|
|
58
|
+
new.ai_agents := coalesce(new.ai_agents, '[]');
|
|
59
|
+
return new;
|
|
60
|
+
end $$;
|
|
61
|
+
create trigger app_ai_default before insert or update on meta.app
|
|
62
|
+
for each row execute function meta.app_ai_default();
|
|
63
|
+
|
|
64
|
+
alter table meta.ai_service drop constraint ai_service_provider_check;
|
|
65
|
+
alter table meta.ai_service add constraint ai_service_provider_check
|
|
66
|
+
check (provider in ('anthropic', 'openai', 'gemini', 'openai_compatible'));
|
|
67
|
+
alter table meta.ai_service add constraint ai_service_compatible_url
|
|
68
|
+
check (provider <> 'openai_compatible' or base_url is not null);
|
|
69
|
+
-- conversations keep the provider's format (Gemini and OpenAI-compatible: OpenAI's)
|
|
70
|
+
alter table meta.ai_conversation drop constraint ai_conversation_provider_check;
|
|
71
|
+
alter table meta.ai_conversation add constraint ai_conversation_provider_check
|
|
72
|
+
check (provider in ('anthropic', 'openai', 'gemini', 'openai_compatible'));
|
|
73
|
+
|
|
74
|
+
create table meta.ai_tool_call (
|
|
75
|
+
id bigserial primary key,
|
|
76
|
+
at timestamptz not null default now(),
|
|
77
|
+
app_id int not null references meta.app on delete cascade,
|
|
78
|
+
page_no int,
|
|
79
|
+
region_id int,
|
|
80
|
+
username text,
|
|
81
|
+
-- null: a tool of the region itself (no agent)
|
|
82
|
+
agent text,
|
|
83
|
+
tool text not null,
|
|
84
|
+
type text not null,
|
|
85
|
+
-- the model's arguments (checked), cut to a few kilobytes
|
|
86
|
+
arguments jsonb not null default '{}',
|
|
87
|
+
status text not null check (status in ('ok', 'error', 'refused')),
|
|
88
|
+
duration_ms int not null default 0,
|
|
89
|
+
message text
|
|
90
|
+
);
|
|
91
|
+
create index on meta.ai_tool_call (app_id, id desc);
|
|
92
|
+
create index on meta.ai_tool_call (at);
|
|
93
|
+
revoke all on meta.ai_tool_call from public;
|
|
94
|
+
comment on table meta.ai_tool_call is 'Tool calls of AI assistants and agents (121), with their arguments; kept 30 days';
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 122: Team Development (APEX: Team Development in the workspace)
|
|
3
|
+
--
|
|
4
|
+
-- An issue tracker per workspace for the team that builds its
|
|
5
|
+
-- applications: issues (title, Markdown body, open or closed) with labels,
|
|
6
|
+
-- a milestone, assignees (the workspace's developers), a link to an
|
|
7
|
+
-- application and one of its pages, comments with @mentions of developers
|
|
8
|
+
-- (shown in the builder, never mailed) and a history of changes. Labels
|
|
9
|
+
-- belong to label groups (e.g. Type: Bug, Feature, Task; Priority: High,
|
|
10
|
+
-- Medium, Low); templates prefill new issues (kind 'issue') or comments
|
|
11
|
+
-- (kind 'response', canned answers). Developers save their own filtered
|
|
12
|
+
-- views of the list.
|
|
13
|
+
--
|
|
14
|
+
-- An issue may come from a user's feedback (meta.feedback, Activity →
|
|
15
|
+
-- Feedback) or a developer comment (meta.dev_comment): linked back.
|
|
16
|
+
--
|
|
17
|
+
-- Workspace data, like the SQL Workshop's scripts: written by the builder
|
|
18
|
+
-- as the owner, never read by the runtime; not part of an application's
|
|
19
|
+
-- export (kept by pgkiln import --replace) but part of the workspace export
|
|
20
|
+
-- (Workspace utilities → Export and import, part "team").
|
|
21
|
+
-- =====================================================================
|
|
22
|
+
|
|
23
|
+
create table meta.team_label_group (
|
|
24
|
+
id bigint generated always as identity primary key,
|
|
25
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
26
|
+
name text not null check (name ~ '^\S(.{0,38}\S)?$'),
|
|
27
|
+
description text check (length(description) <= 500),
|
|
28
|
+
seq int not null default 10
|
|
29
|
+
);
|
|
30
|
+
create unique index team_label_group_name on meta.team_label_group (workspace_id, lower(name));
|
|
31
|
+
|
|
32
|
+
create table meta.team_label (
|
|
33
|
+
id bigint generated always as identity primary key,
|
|
34
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
35
|
+
group_id bigint references meta.team_label_group on delete set null,
|
|
36
|
+
name text not null check (name ~ '^\S(.{0,38}\S)?$'),
|
|
37
|
+
-- a colour of a fixed palette (builder.css draws them); never free CSS
|
|
38
|
+
color text not null default 'gray' check (color in ('red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'gray')),
|
|
39
|
+
description text check (length(description) <= 500),
|
|
40
|
+
seq int not null default 10
|
|
41
|
+
);
|
|
42
|
+
create unique index team_label_name on meta.team_label (workspace_id, lower(name));
|
|
43
|
+
|
|
44
|
+
create table meta.team_milestone (
|
|
45
|
+
id bigint generated always as identity primary key,
|
|
46
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
47
|
+
name text not null check (name ~ '^\S(.{0,38}\S)?$'),
|
|
48
|
+
description text check (length(description) <= 500),
|
|
49
|
+
due_date date,
|
|
50
|
+
status text not null default 'open' check (status in ('open', 'closed')),
|
|
51
|
+
created_at timestamptz not null default now()
|
|
52
|
+
);
|
|
53
|
+
create unique index team_milestone_name on meta.team_milestone (workspace_id, lower(name));
|
|
54
|
+
|
|
55
|
+
create table meta.team_template (
|
|
56
|
+
id bigint generated always as identity primary key,
|
|
57
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
58
|
+
kind text not null check (kind in ('issue', 'response')),
|
|
59
|
+
name text not null check (name ~ '^\S(.{0,58}\S)?$'),
|
|
60
|
+
-- issue templates: the new issue's title and labels (by name)
|
|
61
|
+
title text check (length(title) <= 200),
|
|
62
|
+
body text not null default '' check (length(body) <= 20000),
|
|
63
|
+
labels text[] not null default '{}' check (cardinality(labels) <= 20),
|
|
64
|
+
seq int not null default 10
|
|
65
|
+
);
|
|
66
|
+
create unique index team_template_name on meta.team_template (workspace_id, kind, lower(name));
|
|
67
|
+
|
|
68
|
+
create table meta.team_issue (
|
|
69
|
+
id bigint generated always as identity primary key,
|
|
70
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
71
|
+
title text not null check (length(btrim(title)) between 1 and 200),
|
|
72
|
+
body text not null default '' check (length(body) <= 20000),
|
|
73
|
+
status text not null default 'open' check (status in ('open', 'closed')),
|
|
74
|
+
milestone_id bigint references meta.team_milestone on delete set null,
|
|
75
|
+
-- the application (of the same workspace) and page the issue is about
|
|
76
|
+
app_id int references meta.app on delete set null,
|
|
77
|
+
page_no int check (page_no >= 0),
|
|
78
|
+
-- where it came from: a user's feedback or a developer comment
|
|
79
|
+
feedback_id bigint references meta.feedback on delete set null,
|
|
80
|
+
dev_comment_id int references meta.dev_comment on delete set null,
|
|
81
|
+
created_by text not null,
|
|
82
|
+
created_at timestamptz not null default now(),
|
|
83
|
+
updated_at timestamptz not null default now(),
|
|
84
|
+
closed_by text,
|
|
85
|
+
closed_at timestamptz
|
|
86
|
+
);
|
|
87
|
+
create index team_issue_ws on meta.team_issue (workspace_id, status, updated_at desc);
|
|
88
|
+
create index team_issue_page on meta.team_issue (app_id, page_no) where status = 'open';
|
|
89
|
+
create index team_issue_feedback on meta.team_issue (feedback_id) where feedback_id is not null;
|
|
90
|
+
create index team_issue_comment_src on meta.team_issue (dev_comment_id) where dev_comment_id is not null;
|
|
91
|
+
|
|
92
|
+
create table meta.team_issue_label (
|
|
93
|
+
issue_id bigint not null references meta.team_issue on delete cascade,
|
|
94
|
+
label_id bigint not null references meta.team_label on delete cascade,
|
|
95
|
+
primary key (issue_id, label_id)
|
|
96
|
+
);
|
|
97
|
+
create index on meta.team_issue_label (label_id);
|
|
98
|
+
|
|
99
|
+
create table meta.team_issue_assignee (
|
|
100
|
+
issue_id bigint not null references meta.team_issue on delete cascade,
|
|
101
|
+
username text not null references meta.developer (username) on delete cascade on update cascade,
|
|
102
|
+
primary key (issue_id, username)
|
|
103
|
+
);
|
|
104
|
+
create index on meta.team_issue_assignee (username);
|
|
105
|
+
|
|
106
|
+
create table meta.team_issue_comment (
|
|
107
|
+
id bigint generated always as identity primary key,
|
|
108
|
+
issue_id bigint not null references meta.team_issue on delete cascade,
|
|
109
|
+
author text not null,
|
|
110
|
+
body text not null check (length(btrim(body)) between 1 and 20000),
|
|
111
|
+
created_at timestamptz not null default now()
|
|
112
|
+
);
|
|
113
|
+
create index on meta.team_issue_comment (issue_id, created_at);
|
|
114
|
+
|
|
115
|
+
-- the history of an issue: who changed what, when
|
|
116
|
+
create table meta.team_issue_event (
|
|
117
|
+
id bigint generated always as identity primary key,
|
|
118
|
+
issue_id bigint not null references meta.team_issue on delete cascade,
|
|
119
|
+
at timestamptz not null default now(),
|
|
120
|
+
username text,
|
|
121
|
+
action text not null check (action ~ '^[a-z_]{1,30}$'),
|
|
122
|
+
detail text check (length(detail) <= 1000)
|
|
123
|
+
);
|
|
124
|
+
create index on meta.team_issue_event (issue_id, at);
|
|
125
|
+
|
|
126
|
+
-- @mentions of the workspace's developers in an issue's body or a comment; shown in the builder until seen
|
|
127
|
+
create table meta.team_mention (
|
|
128
|
+
id bigint generated always as identity primary key,
|
|
129
|
+
issue_id bigint not null references meta.team_issue on delete cascade,
|
|
130
|
+
comment_id bigint references meta.team_issue_comment on delete cascade,
|
|
131
|
+
username text not null references meta.developer (username) on delete cascade on update cascade,
|
|
132
|
+
by_user text,
|
|
133
|
+
created_at timestamptz not null default now(),
|
|
134
|
+
seen_at timestamptz
|
|
135
|
+
);
|
|
136
|
+
create index on meta.team_mention (username) where seen_at is null;
|
|
137
|
+
|
|
138
|
+
-- a developer's saved views of the issue list (the filters as a query string)
|
|
139
|
+
create table meta.team_view (
|
|
140
|
+
id bigint generated always as identity primary key,
|
|
141
|
+
workspace_id int not null references meta.workspace on delete cascade,
|
|
142
|
+
username text not null references meta.developer (username) on delete cascade on update cascade,
|
|
143
|
+
name text not null check (name ~ '^\S(.{0,38}\S)?$'),
|
|
144
|
+
query text not null default '' check (length(query) <= 1000),
|
|
145
|
+
created_at timestamptz not null default now()
|
|
146
|
+
);
|
|
147
|
+
create unique index team_view_name on meta.team_view (workspace_id, username, lower(name));
|
|
148
|
+
|
|
149
|
+
-- the label's group, the issue's milestone and its labels belong to the issue's workspace
|
|
150
|
+
create function meta.team_same_workspace() returns trigger
|
|
151
|
+
language plpgsql set search_path = pg_catalog as $$
|
|
152
|
+
begin
|
|
153
|
+
-- (nested: a record field is only read in the branch of its own table)
|
|
154
|
+
if tg_table_name = 'team_label' then
|
|
155
|
+
if new.group_id is not null
|
|
156
|
+
and (select g.workspace_id from meta.team_label_group g where g.id = new.group_id) is distinct from new.workspace_id then
|
|
157
|
+
raise exception 'the label group belongs to another workspace';
|
|
158
|
+
end if;
|
|
159
|
+
elsif tg_table_name = 'team_issue' then
|
|
160
|
+
if new.milestone_id is not null
|
|
161
|
+
and (select m.workspace_id from meta.team_milestone m where m.id = new.milestone_id) is distinct from new.workspace_id then
|
|
162
|
+
raise exception 'the milestone belongs to another workspace';
|
|
163
|
+
end if;
|
|
164
|
+
elsif tg_table_name = 'team_issue_label' then
|
|
165
|
+
if (select l.workspace_id from meta.team_label l where l.id = new.label_id)
|
|
166
|
+
is distinct from (select i.workspace_id from meta.team_issue i where i.id = new.issue_id) then
|
|
167
|
+
raise exception 'the label belongs to another workspace';
|
|
168
|
+
end if;
|
|
169
|
+
end if;
|
|
170
|
+
return new;
|
|
171
|
+
end $$;
|
|
172
|
+
create trigger team_same_workspace before insert or update on meta.team_label for each row execute function meta.team_same_workspace();
|
|
173
|
+
create trigger team_same_workspace before insert or update on meta.team_issue for each row execute function meta.team_same_workspace();
|
|
174
|
+
create trigger team_same_workspace before insert or update on meta.team_issue_label for each row execute function meta.team_same_workspace();
|
|
175
|
+
|
|
176
|
+
revoke all on meta.team_label_group, meta.team_label, meta.team_milestone, meta.team_template, meta.team_issue, meta.team_issue_label,
|
|
177
|
+
meta.team_issue_assignee, meta.team_issue_comment, meta.team_issue_event, meta.team_mention, meta.team_view from public;
|
|
178
|
+
|
|
179
|
+
comment on table meta.team_issue is 'Team Development (122): issues of a workspace; workspace data (the workspace export), not exported with an application';
|
|
180
|
+
comment on table meta.team_label is 'Team Development (122): labels of a workspace''s issues, in label groups, with a colour of a fixed palette';
|
|
181
|
+
comment on table meta.team_label_group is 'Team Development (122): label groups (e.g. Type, Priority)';
|
|
182
|
+
comment on table meta.team_milestone is 'Team Development (122): milestones of a workspace';
|
|
183
|
+
comment on table meta.team_template is 'Team Development (122): issue templates and canned responses';
|
|
184
|
+
comment on table meta.team_issue_event is 'Team Development (122): the history of an issue';
|
|
185
|
+
comment on table meta.team_mention is 'Team Development (122): @mentions of developers, shown in the builder until seen';
|
|
186
|
+
comment on table meta.team_view is 'Team Development (122): saved views of the issue list, per developer';
|