Add rollbacks for associated_object_id
[akkoma] / priv / repo / optional_migrations / pleroma_develop_rollbacks / 20220821004840_change_thread_visibility_to_use_new_object_id_index.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Repo.Migrations.ChangeThreadVisibilityToUseNewObjectIdIndex do
6 use Ecto.Migration
7
8 def up do
9 execute(update_thread_visibility())
10 end
11
12 def down do
13 execute(restore_thread_visibility())
14 end
15
16 def update_thread_visibility do
17 """
18 CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar, local_public varchar default '') RETURNS boolean AS $$
19 DECLARE
20 public varchar := 'https://www.w3.org/ns/activitystreams#Public';
21 child objects%ROWTYPE;
22 activity activities%ROWTYPE;
23 author_fa varchar;
24 valid_recipients varchar[];
25 actor_user_following varchar[];
26 BEGIN
27 --- Fetch actor following
28 SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
29 JOIN users ON users.id = following_relationships.follower_id
30 JOIN users AS following ON following.id = following_relationships.following_id
31 WHERE users.ap_id = actor;
32
33 --- Fetch our initial activity.
34 SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
35
36 LOOP
37 --- Ensure that we have an activity before continuing.
38 --- If we don't, the thread is not satisfiable.
39 IF activity IS NULL THEN
40 RETURN false;
41 END IF;
42
43 --- We only care about Create activities.
44 IF activity.data->>'type' != 'Create' THEN
45 RETURN true;
46 END IF;
47
48 --- Normalize the child object into child.
49 SELECT * INTO child FROM objects
50 INNER JOIN activities ON associated_object_id(activities.data) = objects.data->>'id'
51 WHERE associated_object_id(activity.data) = objects.data->>'id';
52
53 --- Fetch the author's AS2 following collection.
54 SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
55
56 --- Prepare valid recipients array.
57 valid_recipients := ARRAY[actor, public];
58 --- If we specified local public, add it.
59 IF local_public <> '' THEN
60 valid_recipients := valid_recipients || local_public;
61 END IF;
62 IF ARRAY[author_fa] && actor_user_following THEN
63 valid_recipients := valid_recipients || author_fa;
64 END IF;
65
66 --- Check visibility.
67 IF NOT valid_recipients && activity.recipients THEN
68 --- activity not visible, break out of the loop
69 RETURN false;
70 END IF;
71
72 --- If there's a parent, load it and do this all over again.
73 IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
74 SELECT * INTO activity FROM activities
75 INNER JOIN objects ON associated_object_id(activities.data) = objects.data->>'id'
76 WHERE child.data->>'inReplyTo' = objects.data->>'id';
77 ELSE
78 RETURN true;
79 END IF;
80 END LOOP;
81 END;
82 $$ LANGUAGE plpgsql IMMUTABLE;
83 """
84 end
85
86 # priv/repo/migrations/20220509180452_change_thread_visibility_to_be_local_only_aware.exs
87 def restore_thread_visibility do
88 """
89 CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar, local_public varchar default '') RETURNS boolean AS $$
90 DECLARE
91 public varchar := 'https://www.w3.org/ns/activitystreams#Public';
92 child objects%ROWTYPE;
93 activity activities%ROWTYPE;
94 author_fa varchar;
95 valid_recipients varchar[];
96 actor_user_following varchar[];
97 BEGIN
98 --- Fetch actor following
99 SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships
100 JOIN users ON users.id = following_relationships.follower_id
101 JOIN users AS following ON following.id = following_relationships.following_id
102 WHERE users.ap_id = actor;
103
104 --- Fetch our initial activity.
105 SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id;
106
107 LOOP
108 --- Ensure that we have an activity before continuing.
109 --- If we don't, the thread is not satisfiable.
110 IF activity IS NULL THEN
111 RETURN false;
112 END IF;
113
114 --- We only care about Create activities.
115 IF activity.data->>'type' != 'Create' THEN
116 RETURN true;
117 END IF;
118
119 --- Normalize the child object into child.
120 SELECT * INTO child FROM objects
121 INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
122 WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id';
123
124 --- Fetch the author's AS2 following collection.
125 SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor;
126
127 --- Prepare valid recipients array.
128 valid_recipients := ARRAY[actor, public];
129 --- If we specified local public, add it.
130 IF local_public <> '' THEN
131 valid_recipients := valid_recipients || local_public;
132 END IF;
133 IF ARRAY[author_fa] && actor_user_following THEN
134 valid_recipients := valid_recipients || author_fa;
135 END IF;
136
137 --- Check visibility.
138 IF NOT valid_recipients && activity.recipients THEN
139 --- activity not visible, break out of the loop
140 RETURN false;
141 END IF;
142
143 --- If there's a parent, load it and do this all over again.
144 IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN
145 SELECT * INTO activity FROM activities
146 INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id'
147 WHERE child.data->>'inReplyTo' = objects.data->>'id';
148 ELSE
149 RETURN true;
150 END IF;
151 END LOOP;
152 END;
153 $$ LANGUAGE plpgsql IMMUTABLE;
154 """
155 end
156 end