pgkiln 0.44.0 → 0.46.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.
Files changed (91) hide show
  1. package/db/migrations/102_app_computations.sql +345 -0
  2. package/db/migrations/103_shortcuts.sql +341 -0
  3. package/db/migrations/104_report_queries.sql +413 -0
  4. package/db/migrations/105_component_settings_groups.sql +367 -0
  5. package/db/migrations/106_form_designer.sql +102 -0
  6. package/db/migrations/107_app_version.sql +9 -0
  7. package/docs/apex-feature-parity.md +15 -12
  8. package/docs/guide/02-concepts.md +24 -0
  9. package/docs/guide/03-builder.md +69 -13
  10. package/docs/guide/04-pages-and-regions.md +84 -3
  11. package/docs/guide/05-items.md +14 -2
  12. package/docs/guide/06-processing.md +22 -0
  13. package/docs/guide/09-reference.md +14 -1
  14. package/docs/guide/11-from-apex.md +2 -1
  15. package/docs/guide/12-development.md +8 -2
  16. package/docs/guide/16-files.md +23 -0
  17. package/docs/guide/18-cli.md +4 -0
  18. package/examples/hr/hr_61_form_designer.sql +80 -0
  19. package/package.json +1 -1
  20. package/public/app.css +79 -2
  21. package/public/app.js +307 -35
  22. package/public/builder.css +21 -1
  23. package/public/builder.js +67 -0
  24. package/public/sw.js +2 -1
  25. package/src/app.ts +9 -1
  26. package/src/appfiles.ts +18 -2
  27. package/src/builder/advisor.ts +7 -2
  28. package/src/builder/appwizard.ts +22 -6
  29. package/src/builder/arrange.ts +33 -12
  30. package/src/builder/code-editor.ts +7 -2
  31. package/src/builder/components.ts +86 -4
  32. package/src/builder/designer.ts +44 -17
  33. package/src/builder/errors.ts +65 -0
  34. package/src/builder/form-designer.ts +229 -0
  35. package/src/builder/forms.ts +1 -1
  36. package/src/builder/locks.ts +6 -0
  37. package/src/builder/newapp.ts +2 -1
  38. package/src/builder/object-browser.ts +81 -18
  39. package/src/builder/region-help.ts +2 -0
  40. package/src/builder/region-settings.ts +2 -0
  41. package/src/builder/reporter.ts +1 -1
  42. package/src/builder/routes.ts +7 -5
  43. package/src/builder/search.ts +3 -3
  44. package/src/builder/shared.ts +41 -8
  45. package/src/builder/subscriptions.ts +30 -4
  46. package/src/builder/ui.ts +2 -0
  47. package/src/cli/replace.ts +16 -2
  48. package/src/i18n/ar.ts +62 -0
  49. package/src/i18n/cs.ts +62 -0
  50. package/src/i18n/da.ts +62 -0
  51. package/src/i18n/de.ts +62 -0
  52. package/src/i18n/el.ts +62 -0
  53. package/src/i18n/es.ts +62 -0
  54. package/src/i18n/fi.ts +62 -0
  55. package/src/i18n/fr.ts +62 -0
  56. package/src/i18n/he.ts +62 -0
  57. package/src/i18n/it.ts +62 -0
  58. package/src/i18n/ja.ts +62 -0
  59. package/src/i18n/ko.ts +62 -0
  60. package/src/i18n/nb.ts +62 -0
  61. package/src/i18n/pl.ts +62 -0
  62. package/src/i18n/pt.ts +62 -0
  63. package/src/i18n/ru.ts +62 -0
  64. package/src/i18n/sv.ts +62 -0
  65. package/src/i18n/tr.ts +62 -0
  66. package/src/i18n/uk.ts +62 -0
  67. package/src/i18n/zh.ts +62 -0
  68. package/src/i18n.ts +124 -0
  69. package/src/metadata.ts +18 -3
  70. package/src/runtime/component-settings.ts +42 -0
  71. package/src/runtime/context.ts +4 -0
  72. package/src/runtime/devbar.ts +1 -1
  73. package/src/runtime/documents.ts +29 -3
  74. package/src/runtime/engine.ts +2 -2
  75. package/src/runtime/facet-state.ts +10 -5
  76. package/src/runtime/facets.ts +29 -5
  77. package/src/runtime/form-designer.ts +676 -0
  78. package/src/runtime/grid.ts +36 -11
  79. package/src/runtime/items.ts +47 -32
  80. package/src/runtime/lists.ts +1 -1
  81. package/src/runtime/logic.ts +12 -7
  82. package/src/runtime/pdf.ts +2 -1
  83. package/src/runtime/regions.ts +8 -3
  84. package/src/runtime/render.ts +8 -3
  85. package/src/runtime/report.ts +4 -3
  86. package/src/runtime/routes.ts +7 -3
  87. package/src/runtime/shortcuts.ts +43 -0
  88. package/src/runtime/smart-filters.ts +2 -1
  89. package/src/runtime/tasks.ts +6 -3
  90. package/src/security.ts +2 -1
  91. package/src/subscriptions.ts +67 -0
@@ -0,0 +1,341 @@
1
+ -- =====================================================================
2
+ -- 103: shortcuts (APEX: Shared Components → Shortcuts)
3
+ --
4
+ -- meta.shortcut: a named piece of text an application reuses. "NAME" (the
5
+ -- name in double quotes) in a static region's content, an item's help
6
+ -- text and a button's or menu entry's confirm message is replaced by the
7
+ -- shortcut, before &ITEM. substitutions (src/runtime/shortcuts.ts). Only
8
+ -- the names of the application's shortcuts are replaced. Types:
9
+ -- text the value, escaped where it goes into HTML
10
+ -- html the value as HTML
11
+ -- function_body a PL/pgSQL function body that returns the text (HTML),
12
+ -- run as the application's role once per request
13
+ -- export_app / import_app are redefined from 102 to carry shortcuts.
14
+ -- =====================================================================
15
+ create table meta.shortcut (
16
+ id serial primary key,
17
+ app_id int not null references meta.app on delete cascade,
18
+ name text not null check (name ~ '^[A-Z][A-Z0-9_]{0,59}$'),
19
+ type text not null default 'text' check (type in ('text', 'html', 'function_body')),
20
+ value text not null default '',
21
+ comments text,
22
+ unique (app_id, name),
23
+ check (type <> 'function_body' or trim(value) <> '')
24
+ );
25
+ revoke all on meta.shortcut from public;
26
+ grant select on meta.shortcut to pgkiln_runtime;
27
+ comment on table meta.shortcut is 'Shortcuts (103): named texts; "NAME" in static content, help texts and confirm messages is replaced by them';
28
+
29
+ -- (090) an isolated workspace's logins see their own applications' rows only
30
+ alter table meta.shortcut enable row level security;
31
+ create policy workspace on meta.shortcut
32
+ using ((select meta.isolation_off()) or app_id in (select meta.visible_apps()))
33
+ with check ((select meta.isolation_off()) or app_id in (select meta.visible_apps()));
34
+
35
+ create or replace function meta.export_app(p_alias text) returns jsonb
36
+ language sql stable set search_path = meta, pg_catalog as $$
37
+ select jsonb_build_object(
38
+ 'format', 'pgkiln/2',
39
+ -- (083) availability and build status belong to the installation
40
+ 'app', to_jsonb(a) - 'id' - 'created_at' - 'updated_at' - 'availability' - 'availability_message' - 'availability_users' - 'build_status',
41
+ 'authz_schemes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.authz_scheme x where x.app_id = a.id), '[]'),
42
+ 'app_items', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_item x where x.app_id = a.id), '[]'),
43
+ 'app_processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_process x where x.app_id = a.id), '[]'),
44
+ 'lovs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.lov x where x.app_id = a.id), '[]'),
45
+ 'group_roles', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.group_name, x.role) from meta.app_group_role x where x.app_id = a.id), '[]'),
46
+ 'text_messages', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.name, x.language) from meta.text_message x where x.app_id = a.id), '[]'),
47
+ 'translations', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.language, x.source) from meta.translation x where x.app_id = a.id), '[]'),
48
+ 'report_layouts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'logo' || jsonb_build_object('logo', encode(x.logo, 'base64')) order by x.name)
49
+ from meta.report_layout x where x.app_id = a.id), '[]'),
50
+ 'automations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'next_run_at' - 'last_run_at' - 'last_status' - 'code' order by x.name)
51
+ from meta.automation x where x.app_id = a.id), '[]'),
52
+ -- (044) the actions of the automations, by automation name
53
+ 'automation_actions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.automation_name collate "C", x.seq, x.name collate "C")
54
+ from meta.automation_action x where x.app_id = a.id), '[]'),
55
+ 'document_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.document_template x where x.app_id = a.id), '[]'),
56
+ 'task_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.task_definition x where x.app_id = a.id), '[]'),
57
+ 'workflow_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.workflow_definition x where x.app_id = a.id), '[]'),
58
+ 'rest_modules', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.rest_module x where x.app_id = a.id), '[]'),
59
+ 'template_components', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.static_id) from meta.template_component x where x.app_id = a.id), '[]'),
60
+ 'build_options', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.build_option x where x.app_id = a.id), '[]'),
61
+ -- (030) secrets never leave the installation: they are entered again after an import
62
+ -- (050) nor do the password and refresh token of the OAuth2 flows, or the token state
63
+ 'web_credentials', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at' order by x.name)
64
+ from meta.web_credential x where x.app_id = a.id), '[]'),
65
+ -- (050) the synchronisation's state (next and last run) belongs to the installation
66
+ 'rest_sources', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'sync_next_at' - 'sync_last_at' - 'sync_last_status' order by x.name)
67
+ from meta.rest_source x where x.app_id = a.id), '[]'),
68
+ -- (041) data load definitions
69
+ 'data_load_definitions', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.data_load_def x where x.app_id = a.id), '[]'),
70
+ -- (042) lists; entries keep their ids so parents can be remapped on import
71
+ 'lists', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.list x where x.app_id = a.id), '[]'),
72
+ 'list_entries', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.list_name, x.parent_id nulls first, x.seq, x.id) from meta.list_entry x where x.app_id = a.id), '[]'),
73
+ -- (042) supporting objects: install, upgrade and deinstall scripts (never run on import)
74
+ 'supporting_scripts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.supporting_script x where x.app_id = a.id), '[]'),
75
+ -- (069) plug-ins (their files are static files, their templates template components)
76
+ 'plugins', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.plugin x where x.app_id = a.id), '[]'),
77
+ -- (080) search configurations
78
+ 'search_configs', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.search_config x where x.app_id = a.id), '[]'),
79
+ -- (081) e-mail templates
80
+ 'email_templates', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.email_template x where x.app_id = a.id), '[]'),
81
+ -- (086) application settings (with their values) and dynamic translations
82
+ 'app_settings', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.app_setting x where x.app_id = a.id), '[]'),
83
+ 'page_groups', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.page_group x where x.app_id = a.id), '[]'),
84
+ -- (103) shortcuts
85
+ 'shortcuts', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.name) from meta.shortcut x where x.app_id = a.id), '[]'),
86
+ -- (102) application computations
87
+ 'app_computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.seq, x.id) from meta.app_computation x where x.app_id = a.id), '[]'),
88
+ 'dynamic_translations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' order by x.language, x.source collate "C") from meta.dynamic_translation x where x.app_id = a.id), '[]'),
89
+ -- (068) static application files (JavaScript, CSS, images, fonts) as base64
90
+ 'static_files', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'app_id' - 'content' - 'updated_at' || jsonb_build_object('content', translate(encode(x.content, 'base64'), E'\n', '')) order by x.name)
91
+ from meta.static_file x where x.app_id = a.id), '[]'),
92
+ -- nav entries and regions keep their ids, so parents and references can be remapped on import
93
+ 'nav', coalesce((select jsonb_agg(to_jsonb(x) - 'app_id' order by x.parent_id nulls first, x.seq, x.id) from meta.nav_entry x where x.app_id = a.id), '[]'),
94
+ 'pages', coalesce((
95
+ select jsonb_agg(to_jsonb(p) - 'id' - 'app_id' || jsonb_build_object(
96
+ 'regions', coalesce((select jsonb_agg(to_jsonb(r) - 'page_id' order by r.seq, r.id) from meta.region r where r.page_id = p.id), '[]'),
97
+ 'items', coalesce((select jsonb_agg(to_jsonb(i) - 'id' - 'page_id' order by i.seq, i.id) from meta.item i where i.page_id = p.id), '[]'),
98
+ 'buttons', coalesce((select jsonb_agg(to_jsonb(b) - 'id' - 'page_id' order by b.seq, b.id) from meta.button b where b.page_id = p.id), '[]'),
99
+ 'dynamic_actions', coalesce((select jsonb_agg(to_jsonb(d) - 'id' - 'page_id' order by d.seq, d.id) from meta.dynamic_action d where d.page_id = p.id), '[]'),
100
+ 'validations', coalesce((select jsonb_agg(to_jsonb(v) - 'id' - 'page_id' order by v.seq, v.id) from meta.validation v where v.page_id = p.id), '[]'),
101
+ 'processes', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.process x where x.page_id = p.id), '[]'),
102
+ 'computations', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.computation x where x.page_id = p.id), '[]'),
103
+ 'branches', coalesce((select jsonb_agg(to_jsonb(x) - 'id' - 'page_id' order by x.seq, x.id) from meta.branch x where x.page_id = p.id), '[]')
104
+ ) order by p.page_no)
105
+ from meta.page p where p.app_id = a.id), '[]'))
106
+ from meta.app a
107
+ where a.alias = p_alias
108
+ $$;
109
+
110
+ -- ---------------------------------------------------------------- import
111
+ -- Same as 081, plus app_settings and dynamic_translations.
112
+
113
+ create or replace function meta.import_app(p_doc jsonb, p_alias text default null) returns int
114
+ language plpgsql set search_path = meta, pg_catalog as $$
115
+ declare
116
+ v_app_id int;
117
+ v_page_id int;
118
+ v_page jsonb;
119
+ v_e jsonb;
120
+ v_rmap jsonb;
121
+ v_nmap jsonb := '{}';
122
+ v_lmap jsonb := '{}';
123
+ v_new_id int;
124
+ begin
125
+ if p_doc->>'format' is distinct from 'pgkiln/2' then
126
+ raise exception 'unsupported export format %', coalesce(p_doc->>'format', '(none)');
127
+ end if;
128
+
129
+ insert into meta.app
130
+ select (jsonb_populate_record(null::meta.app, p_doc->'app' || jsonb_build_object(
131
+ 'id', nextval('meta.app_id_seq'),
132
+ 'alias', coalesce(p_alias, p_doc->'app'->>'alias'),
133
+ 'created_at', now(), 'updated_at', now()))).*
134
+ returning id into v_app_id;
135
+
136
+ insert into meta.authz_scheme
137
+ select (jsonb_populate_record(null::meta.authz_scheme, e || jsonb_build_object('id', nextval('meta.authz_scheme_id_seq'), 'app_id', v_app_id))).*
138
+ from jsonb_array_elements(coalesce(p_doc->'authz_schemes', '[]')) e;
139
+ insert into meta.app_item
140
+ select (jsonb_populate_record(null::meta.app_item, e || jsonb_build_object('id', nextval('meta.app_item_id_seq'), 'app_id', v_app_id))).*
141
+ from jsonb_array_elements(coalesce(p_doc->'app_items', '[]')) e;
142
+ insert into meta.app_process
143
+ select (jsonb_populate_record(null::meta.app_process, e || jsonb_build_object('id', nextval('meta.app_process_id_seq'), 'app_id', v_app_id))).*
144
+ from jsonb_array_elements(coalesce(p_doc->'app_processes', '[]')) e;
145
+ insert into meta.lov
146
+ select (jsonb_populate_record(null::meta.lov, e || jsonb_build_object('id', nextval('meta.lov_id_seq'), 'app_id', v_app_id))).*
147
+ from jsonb_array_elements(coalesce(p_doc->'lovs', '[]')) e;
148
+ insert into meta.app_group_role
149
+ select (jsonb_populate_record(null::meta.app_group_role, e || jsonb_build_object('app_id', v_app_id))).*
150
+ from jsonb_array_elements(coalesce(p_doc->'group_roles', '[]')) e;
151
+ insert into meta.text_message
152
+ select (jsonb_populate_record(null::meta.text_message, e || jsonb_build_object('app_id', v_app_id))).*
153
+ from jsonb_array_elements(coalesce(p_doc->'text_messages', '[]')) e;
154
+ insert into meta.translation
155
+ select (jsonb_populate_record(null::meta.translation, e || jsonb_build_object('app_id', v_app_id))).*
156
+ from jsonb_array_elements(coalesce(p_doc->'translations', '[]')) e;
157
+ insert into meta.report_layout
158
+ select (jsonb_populate_record(null::meta.report_layout, (e - 'logo') || jsonb_build_object(
159
+ 'id', nextval('meta.report_layout_id_seq'), 'app_id', v_app_id,
160
+ 'logo', null))).*
161
+ from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e;
162
+ update meta.report_layout l
163
+ set logo = decode(e->>'logo', 'base64')
164
+ from jsonb_array_elements(coalesce(p_doc->'report_layouts', '[]')) e
165
+ where l.app_id = v_app_id and l.name = e->>'name' and e->>'logo' is not null;
166
+ -- imported automations start switched off: the copy must not run the original's jobs unasked.
167
+ -- (044) a file with actions: the automations' code is ignored; an older file: code becomes the single action
168
+ insert into meta.automation
169
+ select (jsonb_populate_record(null::meta.automation, '{"error_handling": "stop"}'::jsonb
170
+ || jsonb_strip_nulls(case when p_doc ? 'automation_actions' then e - 'code' else e end)
171
+ || jsonb_build_object('id', nextval('meta.automation_id_seq'), 'app_id', v_app_id, 'enabled', false))).*
172
+ from jsonb_array_elements(coalesce(p_doc->'automations', '[]')) e;
173
+ insert into meta.automation_action
174
+ select (jsonb_populate_record(null::meta.automation_action, '{"seq": 10}'::jsonb || jsonb_strip_nulls(e)
175
+ || jsonb_build_object('id', nextval('meta.automation_action_id_seq'), 'app_id', v_app_id))).*
176
+ from jsonb_array_elements(coalesce(p_doc->'automation_actions', '[]')) e;
177
+ insert into meta.document_template
178
+ select (jsonb_populate_record(null::meta.document_template, e || jsonb_build_object('id', nextval('meta.document_template_id_seq'), 'app_id', v_app_id))).*
179
+ from jsonb_array_elements(coalesce(p_doc->'document_templates', '[]')) e;
180
+ insert into meta.task_definition
181
+ select (jsonb_populate_record(null::meta.task_definition, e || jsonb_build_object('id', nextval('meta.task_definition_id_seq'), 'app_id', v_app_id))).*
182
+ from jsonb_array_elements(coalesce(p_doc->'task_definitions', '[]')) e;
183
+ insert into meta.workflow_definition
184
+ select (jsonb_populate_record(null::meta.workflow_definition, e || jsonb_build_object('id', nextval('meta.workflow_definition_id_seq'), 'app_id', v_app_id))).*
185
+ from jsonb_array_elements(coalesce(p_doc->'workflow_definitions', '[]')) e;
186
+ insert into meta.rest_module
187
+ select (jsonb_populate_record(null::meta.rest_module, e || jsonb_build_object('id', nextval('meta.rest_module_id_seq'), 'app_id', v_app_id))).*
188
+ from jsonb_array_elements(coalesce(p_doc->'rest_modules', '[]')) e;
189
+ -- (028) template components: regions and report columns refer to them by static id
190
+ insert into meta.template_component
191
+ select (jsonb_populate_record(null::meta.template_component, e || jsonb_build_object('id', nextval('meta.template_component_id_seq'), 'app_id', v_app_id))).*
192
+ from jsonb_array_elements(coalesce(p_doc->'template_components', '[]')) e;
193
+ -- (029) build options (components name them by name)
194
+ insert into meta.build_option
195
+ select (jsonb_populate_record(null::meta.build_option, e || jsonb_build_object('id', nextval('meta.build_option_id_seq'), 'app_id', v_app_id))).*
196
+ from jsonb_array_elements(coalesce(p_doc->'build_options', '[]')) e;
197
+ -- (030) web credentials arrive without a secret, whatever the document holds
198
+ -- (050) nor a password or refresh token
199
+ insert into meta.web_credential
200
+ select (jsonb_populate_record(null::meta.web_credential, '{"type": "basic", "valid_for": [], "grant_type": "client_credentials"}'::jsonb
201
+ || jsonb_strip_nulls(e - 'secret_enc' - 'password_enc' - 'refresh_token_enc' - 'token_refreshed_at')
202
+ || jsonb_build_object('id', nextval('meta.web_credential_id_seq'), 'app_id', v_app_id))).*
203
+ from jsonb_array_elements(coalesce(p_doc->'web_credentials', '[]')) e;
204
+ -- (050) write-back operations and synchronisation; an imported synchronisation starts switched off
205
+ insert into meta.rest_source
206
+ select (jsonb_populate_record(null::meta.rest_source, '{"method": "GET", "headers": {}, "params": [], "columns": [], "cache_seconds": 0, "timeout_s": 10, "max_rows": 1000,
207
+ "operations": {}, "key_columns": [], "sync_mode": "merge", "sync_delete": false, "sync_time_zone": "UTC"}'::jsonb
208
+ || jsonb_strip_nulls(e - 'sync_next_at' - 'sync_last_at' - 'sync_last_status')
209
+ || jsonb_build_object('id', nextval('meta.rest_source_id_seq'), 'app_id', v_app_id, 'sync_enabled', false))).*
210
+ from jsonb_array_elements(coalesce(p_doc->'rest_sources', '[]')) e;
211
+
212
+ -- (041) data load definitions (the data_load process names them)
213
+ insert into meta.data_load_def
214
+ select (jsonb_populate_record(null::meta.data_load_def, '{"format": "auto", "headers": true, "mode": "append", "skip_errors": false, "columns": []}'::jsonb
215
+ || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.data_load_def_id_seq'), 'app_id', v_app_id))).*
216
+ from jsonb_array_elements(coalesce(p_doc->'data_load_definitions', '[]')) e;
217
+
218
+ -- (042) lists and their entries, supporting scripts. Entries are inserted
219
+ -- without a parent first and the parents set afterwards, so the order of
220
+ -- the entries in the document doesn't matter (a moved entry may have a
221
+ -- higher id than its children).
222
+ insert into meta.list
223
+ select (jsonb_populate_record(null::meta.list, '{"type": "static"}'::jsonb || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.list_id_seq'), 'app_id', v_app_id))).*
224
+ from jsonb_array_elements(coalesce(p_doc->'lists', '[]')) e;
225
+ for v_e in select * from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) loop
226
+ insert into meta.list_entry
227
+ select (jsonb_populate_record(null::meta.list_entry, '{"target_items": {}}'::jsonb || jsonb_strip_nulls(v_e) || jsonb_build_object(
228
+ 'id', nextval('meta.list_entry_id_seq'), 'app_id', v_app_id, 'parent_id', null))).*
229
+ returning id into v_new_id;
230
+ if v_e->>'id' is not null then
231
+ v_lmap := v_lmap || jsonb_build_object(v_e->>'id', v_new_id);
232
+ end if;
233
+ end loop;
234
+ update meta.list_entry x
235
+ set parent_id = (v_lmap->>(e->>'parent_id'))::int
236
+ from jsonb_array_elements(coalesce(p_doc->'list_entries', '[]')) e
237
+ where e->>'parent_id' is not null and v_lmap ? (e->>'parent_id') and v_lmap ? (e->>'id')
238
+ and x.id = (v_lmap->>(e->>'id'))::int;
239
+ insert into meta.supporting_script
240
+ select (jsonb_populate_record(null::meta.supporting_script, '{"kind": "install", "seq": 10, "script": ""}'::jsonb || jsonb_strip_nulls(e)
241
+ || jsonb_build_object('id', nextval('meta.supporting_script_id_seq'), 'app_id', v_app_id))).*
242
+ from jsonb_array_elements(coalesce(p_doc->'supporting_scripts', '[]')) e;
243
+
244
+ -- (069) plug-ins
245
+ insert into meta.plugin
246
+ select (jsonb_populate_record(null::meta.plugin, e || jsonb_build_object('id', nextval('meta.plugin_id_seq'), 'app_id', v_app_id))).*
247
+ from jsonb_array_elements(coalesce(p_doc->'plugins', '[]')) e;
248
+
249
+ -- (080) search configurations (search regions name them)
250
+ insert into meta.search_config
251
+ select (jsonb_populate_record(null::meta.search_config, '{"seq": 10, "source_type": "table", "searchable_columns": [], "max_results": 10}'::jsonb
252
+ || jsonb_strip_nulls(e) || jsonb_build_object('id', nextval('meta.search_config_id_seq'), 'app_id', v_app_id))).*
253
+ from jsonb_array_elements(coalesce(p_doc->'search_configs', '[]')) e;
254
+
255
+ -- (081) e-mail templates (meta.send_mail_template and the send_email process name them)
256
+ insert into meta.email_template
257
+ select (jsonb_populate_record(null::meta.email_template, e || jsonb_build_object('id', nextval('meta.email_template_id_seq'), 'app_id', v_app_id))).*
258
+ from jsonb_array_elements(coalesce(p_doc->'email_templates', '[]')) e;
259
+
260
+ -- (103) shortcuts
261
+ insert into meta.shortcut
262
+ select (jsonb_populate_record(null::meta.shortcut, '{"type": "text"}'::jsonb || jsonb_strip_nulls(e)
263
+ || jsonb_build_object('id', nextval('meta.shortcut_id_seq'), 'app_id', v_app_id))).*
264
+ from jsonb_array_elements(coalesce(p_doc->'shortcuts', '[]')) e;
265
+
266
+ -- (102) application computations
267
+ insert into meta.app_computation
268
+ select (jsonb_populate_record(null::meta.app_computation, '{"seq": 10, "point": "before_header", "type": "static"}'::jsonb || jsonb_strip_nulls(e)
269
+ || jsonb_build_object('id', nextval('meta.app_computation_id_seq'), 'app_id', v_app_id))).*
270
+ from jsonb_array_elements(coalesce(p_doc->'app_computations', '[]')) e;
271
+
272
+ -- (089) page groups
273
+ insert into meta.page_group
274
+ select (jsonb_populate_record(null::meta.page_group, e || jsonb_build_object('id', nextval('meta.page_group_id_seq'), 'app_id', v_app_id))).*
275
+ from jsonb_array_elements(coalesce(p_doc->'page_groups', '[]')) e;
276
+
277
+ -- (086) application settings and dynamic translations
278
+ insert into meta.app_setting
279
+ select (jsonb_populate_record(null::meta.app_setting, e || jsonb_build_object('id', nextval('meta.app_setting_id_seq'), 'app_id', v_app_id))).*
280
+ from jsonb_array_elements(coalesce(p_doc->'app_settings', '[]')) e;
281
+ insert into meta.dynamic_translation
282
+ select (jsonb_populate_record(null::meta.dynamic_translation, e || jsonb_build_object('id', nextval('meta.dynamic_translation_id_seq'), 'app_id', v_app_id))).*
283
+ from jsonb_array_elements(coalesce(p_doc->'dynamic_translations', '[]')) e;
284
+
285
+ -- (068) static application files
286
+ insert into meta.static_file (app_id, name, mime, content, updated_at)
287
+ select v_app_id, e->>'name', e->>'mime', decode(coalesce(e->>'content', ''), 'base64'), now()
288
+ from jsonb_array_elements(coalesce(p_doc->'static_files', '[]')) e;
289
+
290
+ for v_e in select * from jsonb_array_elements(coalesce(p_doc->'nav', '[]')) loop
291
+ insert into meta.nav_entry
292
+ select (jsonb_populate_record(null::meta.nav_entry, v_e || jsonb_build_object(
293
+ 'id', nextval('meta.nav_entry_id_seq'), 'app_id', v_app_id,
294
+ 'parent_id', v_nmap->>(v_e->>'parent_id')))).*
295
+ returning id into v_new_id;
296
+ v_nmap := v_nmap || jsonb_build_object(v_e->>'id', v_new_id);
297
+ end loop;
298
+
299
+ for v_page in select * from jsonb_array_elements(coalesce(p_doc->'pages', '[]')) loop
300
+ insert into meta.page
301
+ select (jsonb_populate_record(null::meta.page, v_page || jsonb_build_object('id', nextval('meta.page_id_seq'), 'app_id', v_app_id))).*
302
+ returning id into v_page_id;
303
+
304
+ v_rmap := '{}';
305
+ for v_e in select * from jsonb_array_elements(coalesce(v_page->'regions', '[]')) loop
306
+ insert into meta.region
307
+ select (jsonb_populate_record(null::meta.region, v_e || jsonb_build_object('id', nextval('meta.region_id_seq'), 'page_id', v_page_id))).*
308
+ returning id into v_new_id;
309
+ v_rmap := v_rmap || jsonb_build_object(v_e->>'id', v_new_id);
310
+ end loop;
311
+ -- facet and map regions point at their report region by id
312
+ update meta.region r
313
+ set config = jsonb_set(r.config, '{report}', to_jsonb((v_rmap->>(r.config->>'report'))::int))
314
+ where r.page_id = v_page_id and r.type in ('facets', 'smart_filters', 'map') and v_rmap ? (r.config->>'report');
315
+
316
+ insert into meta.item
317
+ select (jsonb_populate_record(null::meta.item, e || jsonb_build_object('id', nextval('meta.item_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
318
+ from jsonb_array_elements(coalesce(v_page->'items', '[]')) e;
319
+ insert into meta.button
320
+ select (jsonb_populate_record(null::meta.button, e || jsonb_build_object('id', nextval('meta.button_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
321
+ from jsonb_array_elements(coalesce(v_page->'buttons', '[]')) e;
322
+ insert into meta.dynamic_action
323
+ select (jsonb_populate_record(null::meta.dynamic_action, e || jsonb_build_object('id', nextval('meta.dynamic_action_id_seq'), 'page_id', v_page_id, 'affected_region_id', v_rmap->>(e->>'affected_region_id')))).*
324
+ from jsonb_array_elements(coalesce(v_page->'dynamic_actions', '[]')) e;
325
+ insert into meta.validation
326
+ select (jsonb_populate_record(null::meta.validation, e || jsonb_build_object('id', nextval('meta.validation_id_seq'), 'page_id', v_page_id))).*
327
+ from jsonb_array_elements(coalesce(v_page->'validations', '[]')) e;
328
+ insert into meta.process
329
+ select (jsonb_populate_record(null::meta.process, e || jsonb_build_object('id', nextval('meta.process_id_seq'), 'page_id', v_page_id, 'region_id', v_rmap->>(e->>'region_id')))).*
330
+ from jsonb_array_elements(coalesce(v_page->'processes', '[]')) e;
331
+ insert into meta.computation
332
+ select (jsonb_populate_record(null::meta.computation, e || jsonb_build_object('id', nextval('meta.computation_id_seq'), 'page_id', v_page_id))).*
333
+ from jsonb_array_elements(coalesce(v_page->'computations', '[]')) e;
334
+ insert into meta.branch
335
+ select (jsonb_populate_record(null::meta.branch, e || jsonb_build_object('id', nextval('meta.branch_id_seq'), 'page_id', v_page_id))).*
336
+ from jsonb_array_elements(coalesce(v_page->'branches', '[]')) e;
337
+ end loop;
338
+
339
+ return v_app_id;
340
+ end
341
+ $$;