Skip to content

Commit 16bb761

Browse files
authored
ENG-1311 Unify ConceptAccess and ContentAccess tables (#712)
* eng-1311 collapse ContentAccess and ConceptAccess into ResourceAccess
1 parent 8315e94 commit 16bb761

File tree

4 files changed

+339
-179
lines changed

4 files changed

+339
-179
lines changed

packages/database/src/dbTypes.ts

Lines changed: 26 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -226,36 +226,6 @@ export type Database = {
226226
},
227227
]
228228
}
229-
ConceptAccess: {
230-
Row: {
231-
account_uid: string
232-
concept_id: number
233-
}
234-
Insert: {
235-
account_uid: string
236-
concept_id: number
237-
}
238-
Update: {
239-
account_uid?: string
240-
concept_id?: number
241-
}
242-
Relationships: [
243-
{
244-
foreignKeyName: "ConceptAccess_concept_id_fkey"
245-
columns: ["concept_id"]
246-
isOneToOne: false
247-
referencedRelation: "Concept"
248-
referencedColumns: ["id"]
249-
},
250-
{
251-
foreignKeyName: "ConceptAccess_concept_id_fkey"
252-
columns: ["concept_id"]
253-
isOneToOne: false
254-
referencedRelation: "my_concepts"
255-
referencedColumns: ["id"]
256-
},
257-
]
258-
}
259229
Content: {
260230
Row: {
261231
author_id: number | null
@@ -433,43 +403,6 @@ export type Database = {
433403
},
434404
]
435405
}
436-
ContentAccess: {
437-
Row: {
438-
account_uid: string
439-
content_id: number
440-
}
441-
Insert: {
442-
account_uid: string
443-
content_id: number
444-
}
445-
Update: {
446-
account_uid?: string
447-
content_id?: number
448-
}
449-
Relationships: [
450-
{
451-
foreignKeyName: "ContentAccess_content_id_fkey"
452-
columns: ["content_id"]
453-
isOneToOne: false
454-
referencedRelation: "Content"
455-
referencedColumns: ["id"]
456-
},
457-
{
458-
foreignKeyName: "ContentAccess_content_id_fkey"
459-
columns: ["content_id"]
460-
isOneToOne: false
461-
referencedRelation: "my_contents"
462-
referencedColumns: ["id"]
463-
},
464-
{
465-
foreignKeyName: "ContentAccess_content_id_fkey"
466-
columns: ["content_id"]
467-
isOneToOne: false
468-
referencedRelation: "my_contents_with_embedding_openai_text_embedding_3_small_1536"
469-
referencedColumns: ["id"]
470-
},
471-
]
472-
}
473406
ContentEmbedding_openai_text_embedding_3_small_1536: {
474407
Row: {
475408
model: Database["public"]["Enums"]["EmbeddingName"]
@@ -676,6 +609,24 @@ export type Database = {
676609
}
677610
Relationships: []
678611
}
612+
ResourceAccess: {
613+
Row: {
614+
account_uid: string
615+
source_local_id: string
616+
space_id: number
617+
}
618+
Insert: {
619+
account_uid: string
620+
source_local_id: string
621+
space_id: number
622+
}
623+
Update: {
624+
account_uid?: string
625+
source_local_id?: string
626+
space_id?: number
627+
}
628+
Relationships: []
629+
}
679630
Space: {
680631
Row: {
681632
id: number
@@ -1346,8 +1297,10 @@ export type Database = {
13461297
}
13471298
}
13481299
can_access_account: { Args: { account_uid: string }; Returns: boolean }
1349-
can_view_specific_concept: { Args: { id: number }; Returns: boolean }
1350-
can_view_specific_content: { Args: { id: number }; Returns: boolean }
1300+
can_view_specific_resource: {
1301+
Args: { source_local_id_: string; space_id_: number }
1302+
Returns: boolean
1303+
}
13511304
compute_arity_local: {
13521305
Args: { lit_content: Json; schema_id: number }
13531306
Returns: number
@@ -1524,6 +1477,10 @@ export type Database = {
15241477
}
15251478
}
15261479
is_group_admin: { Args: { group_id_: string }; Returns: boolean }
1480+
is_last_local_reference: {
1481+
Args: { source_local_id_: string; space_id_: number }
1482+
Returns: boolean
1483+
}
15271484
is_my_account: { Args: { account_id: number }; Returns: boolean }
15281485
match_content_embeddings: {
15291486
Args: {
@@ -1933,4 +1890,3 @@ export const Constants = {
19331890
},
19341891
},
19351892
} as const
1936-
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
DROP TABLE public."ConceptAccess" CASCADE;
2+
3+
ALTER TABLE public."ContentAccess" RENAME TO "ResourceAccess";
4+
ALTER TABLE public."ResourceAccess" RENAME CONSTRAINT "ContentAccess_pkey" TO "ResourceAccess_pkey";
5+
ALTER TABLE public."ResourceAccess" RENAME CONSTRAINT "ContentAccess_account_uid_fkey" TO "ResourceAccess_account_uid_fkey";
6+
7+
ALTER TABLE public."ResourceAccess" ADD COLUMN space_id BIGINT;
8+
ALTER TABLE public."ResourceAccess" ADD COLUMN source_local_id CHARACTER VARYING;
9+
10+
COMMENT ON COLUMN public."ResourceAccess".space_id IS 'The space_id of the content item for which access is granted';
11+
COMMENT ON COLUMN public."ResourceAccess".source_local_id IS 'The source_local_id of the content item for which access is granted';
12+
13+
UPDATE public."ResourceAccess" AS ca
14+
SET space_id = ct.space_id, source_local_id = ct.source_local_id
15+
FROM public."Content" AS ct WHERE ct.id = content_id;
16+
17+
ALTER TABLE public."ResourceAccess" DROP COLUMN content_id CASCADE;
18+
-- cascades to Content policies, indices, primary key...
19+
20+
ALTER TABLE public."ResourceAccess" ALTER COLUMN space_id SET NOT NULL;
21+
ALTER TABLE public."ResourceAccess" ALTER COLUMN source_local_id SET NOT NULL;
22+
23+
ALTER TABLE ONLY public."ResourceAccess"
24+
ADD CONSTRAINT "ResourceAccess_pkey" PRIMARY KEY (account_uid, source_local_id, space_id);
25+
26+
CREATE INDEX resource_access_content_local_id_idx ON public."ResourceAccess" (source_local_id, space_id);
27+
28+
CREATE OR REPLACE FUNCTION public.can_view_specific_resource(space_id_ BIGINT, source_local_id_ VARCHAR) RETURNS BOOLEAN
29+
STABLE SECURITY DEFINER
30+
SET search_path = ''
31+
LANGUAGE sql
32+
AS $$
33+
SELECT EXISTS(
34+
SELECT true FROM public."ResourceAccess"
35+
JOIN public.my_user_accounts() ON (account_uid=my_user_accounts)
36+
WHERE space_id=space_id_
37+
AND source_local_id = source_local_id_
38+
LIMIT 1);
39+
$$;
40+
41+
CREATE OR REPLACE VIEW public.my_documents AS
42+
SELECT
43+
id,
44+
space_id,
45+
source_local_id,
46+
url,
47+
"created",
48+
metadata,
49+
last_modified,
50+
author_id,
51+
contents
52+
FROM public."Document" WHERE space_id = any(public.my_space_ids())
53+
OR public.can_view_specific_resource(space_id, source_local_id);
54+
55+
CREATE OR REPLACE VIEW public.my_contents AS
56+
SELECT
57+
id,
58+
document_id,
59+
source_local_id,
60+
variant,
61+
author_id,
62+
creator_id,
63+
created,
64+
text,
65+
metadata,
66+
scale,
67+
space_id,
68+
last_modified,
69+
part_of_id
70+
FROM public."Content"
71+
WHERE (
72+
space_id = any(public.my_space_ids())
73+
OR public.can_view_specific_resource(space_id, source_local_id)
74+
);
75+
76+
DROP POLICY IF EXISTS document_policy ON public."Document";
77+
DROP POLICY IF EXISTS document_select_policy ON public."Document";
78+
CREATE POLICY document_select_policy ON public."Document" FOR SELECT USING (public.in_space(space_id) OR public.can_view_specific_resource(space_id, source_local_id));
79+
DROP POLICY IF EXISTS document_delete_policy ON public."Document";
80+
CREATE POLICY document_delete_policy ON public."Document" FOR DELETE USING (public.in_space(space_id));
81+
DROP POLICY IF EXISTS document_insert_policy ON public."Document";
82+
CREATE POLICY document_insert_policy ON public."Document" FOR INSERT WITH CHECK (public.in_space(space_id));
83+
DROP POLICY IF EXISTS document_update_policy ON public."Document";
84+
CREATE POLICY document_update_policy ON public."Document" FOR UPDATE USING (public.in_space(space_id));
85+
86+
DROP POLICY IF EXISTS content_select_policy ON public."Content";
87+
CREATE POLICY content_select_policy ON public."Content" FOR SELECT USING (public.in_space(space_id) OR public.can_view_specific_resource(space_id, source_local_id));
88+
89+
DROP POLICY IF EXISTS content_access_select_policy ON public."ResourceAccess";
90+
DROP POLICY IF EXISTS content_access_delete_policy ON public."ResourceAccess";
91+
DROP POLICY IF EXISTS content_access_insert_policy ON public."ResourceAccess";
92+
DROP POLICY IF EXISTS content_access_update_policy ON public."ResourceAccess";
93+
94+
DROP POLICY IF EXISTS resource_access_select_policy ON public."ResourceAccess";
95+
CREATE POLICY resource_access_select_policy ON public."ResourceAccess" FOR SELECT USING (public.in_space(space_id) OR public.can_access_account(account_uid));
96+
DROP POLICY IF EXISTS resource_access_delete_policy ON public."ResourceAccess";
97+
CREATE POLICY resource_access_delete_policy ON public."ResourceAccess" FOR DELETE USING (public.editor_in_space(space_id) OR public.can_access_account(account_uid));
98+
DROP POLICY IF EXISTS resource_access_insert_policy ON public."ResourceAccess";
99+
CREATE POLICY resource_access_insert_policy ON public."ResourceAccess" FOR INSERT WITH CHECK (public.editor_in_space(space_id));
100+
DROP POLICY IF EXISTS resource_access_update_policy ON public."ResourceAccess";
101+
CREATE POLICY resource_access_update_policy ON public."ResourceAccess" FOR UPDATE USING (public.editor_in_space(space_id));
102+
103+
DROP FUNCTION public.can_view_specific_content(BIGINT);
104+
105+
CREATE OR REPLACE VIEW public.my_concepts AS
106+
SELECT
107+
id,
108+
epistemic_status,
109+
name,
110+
description,
111+
author_id,
112+
created,
113+
last_modified,
114+
space_id,
115+
arity,
116+
schema_id,
117+
literal_content,
118+
reference_content,
119+
refs,
120+
is_schema,
121+
source_local_id
122+
FROM public."Concept"
123+
WHERE (
124+
space_id = any(public.my_space_ids())
125+
OR public.can_view_specific_resource(space_id, source_local_id)
126+
);
127+
128+
129+
DROP POLICY IF EXISTS concept_select_policy ON public."Concept";
130+
CREATE POLICY concept_select_policy ON public."Concept" FOR SELECT USING (public.in_space(space_id) OR public.can_view_specific_resource(space_id, source_local_id));
131+
132+
DROP FUNCTION public.can_view_specific_concept(BIGINT);
133+
134+
CREATE OR REPLACE FUNCTION public.is_last_local_reference(space_id_ BIGINT, source_local_id_ VARCHAR) RETURNS boolean
135+
STABLE
136+
SET search_path = ''
137+
SECURITY DEFINER
138+
LANGUAGE sql
139+
AS $$
140+
SELECT NOT EXISTS (SELECT id FROM public."Content" WHERE space_id=space_id_ AND source_local_id=source_local_id_ LIMIT 1)
141+
AND NOT EXISTS (SELECT id FROM public."Concept" WHERE space_id=space_id_ AND source_local_id=source_local_id_ LIMIT 1)
142+
AND NOT EXISTS (SELECT id FROM public."Document" WHERE space_id=space_id_ AND source_local_id=source_local_id_ LIMIT 1);
143+
$$;
144+
145+
CREATE OR REPLACE FUNCTION on_delete_local_reference() RETURNS TRIGGER
146+
SET search_path = ''
147+
SECURITY DEFINER
148+
LANGUAGE plpgsql
149+
AS $$
150+
BEGIN
151+
IF public.is_last_local_reference(OLD.space_id, OLD.source_local_id) THEN
152+
DELETE FROM public."ResourceAccess" WHERE space_id=OLD.space_id AND source_local_id=OLD.source_local_id;
153+
END IF;
154+
RETURN OLD;
155+
END;
156+
$$;
157+
158+
CREATE TRIGGER on_delete_content_trigger AFTER DELETE ON public."Content" FOR EACH ROW EXECUTE FUNCTION public.on_delete_local_reference();
159+
CREATE TRIGGER on_delete_concept_trigger AFTER DELETE ON public."Concept" FOR EACH ROW EXECUTE FUNCTION public.on_delete_local_reference();
160+
CREATE TRIGGER on_delete_document_trigger AFTER DELETE ON public."Document" FOR EACH ROW EXECUTE FUNCTION public.on_delete_local_reference();
161+
162+
CREATE OR REPLACE FUNCTION on_update_local_reference() RETURNS TRIGGER
163+
SET search_path = ''
164+
SECURITY DEFINER
165+
LANGUAGE plpgsql
166+
AS $$
167+
BEGIN
168+
IF (OLD.space_id IS DISTINCT FROM NEW.space_id OR
169+
OLD.source_local_id IS DISTINCT FROM NEW.source_local_id)
170+
AND public.is_last_local_reference(OLD.space_id, OLD.source_local_id) THEN
171+
DELETE FROM public."ResourceAccess" WHERE space_id=OLD.space_id AND source_local_id=OLD.source_local_id;
172+
END IF;
173+
RETURN NEW;
174+
END;
175+
$$;
176+
177+
CREATE TRIGGER on_update_content_trigger AFTER UPDATE ON public."Content" FOR EACH ROW EXECUTE FUNCTION public.on_update_local_reference();
178+
CREATE TRIGGER on_update_concept_trigger AFTER UPDATE ON public."Concept" FOR EACH ROW EXECUTE FUNCTION public.on_update_local_reference();
179+
CREATE TRIGGER on_update_document_trigger AFTER UPDATE ON public."Document" FOR EACH ROW EXECUTE FUNCTION public.on_update_local_reference();
180+
181+
CREATE OR REPLACE FUNCTION public.on_delete_space_revoke_local_access() RETURNS TRIGGER
182+
SET search_path = ''
183+
SECURITY DEFINER
184+
LANGUAGE plpgsql
185+
AS $$
186+
BEGIN
187+
DELETE FROM public."ResourceAccess" WHERE space_id=OLD.id;
188+
RETURN OLD;
189+
END;
190+
$$;
191+
192+
CREATE TRIGGER on_delete_space_revoke_access_trigger AFTER DELETE ON public."Space" FOR EACH ROW EXECUTE FUNCTION public.on_delete_space_revoke_local_access();

0 commit comments

Comments
 (0)