ADD model related code for ActivityPub subscription
What does this MR do and why?
This MR has been split off from ADD ActivityPub subscription. See there if you want a complete description and overall look of the feature.
Those changes add everything we need on the database part to implement the subscription of an external user to an internal resource.
We're implementing base architecture for all ActivityPub features
through a first used case, being the releases actor. After discussions
on the blueprint,
the models were migrated from using STI to using an abstract model,
so we have here this abstract model (ActivityPub::Subscription
)
from which all other models will inherit, using their own database
table - here activity_pub_releases_subscriptions
.
The subscription happens in several steps, which is why there is those
status
and payload
fields:
- the record is created with the
requested
status, we save the request JSON payload of the Follow activity (we're going to need it later) and store thesubscriber_url
, which is the profile URL for the external subscriber - the background job retrieve the subscriber inbox URL by fetching the profile page, and stores it in the record
- the background job sends out an
Accept
activity, which must contains a copy of the originalFollow
activity - the record status is changed to
accepted
.
status attribute
Worth noting is that I added a denied
status that won't be used in this
feature. ActivityPub allows to reply to a Follow
request with a Reject
activity rather than an Accept
one. The main purpose is to notify the
user that no, we won't be pushing activities out to let them know of
updates (but if the actor is public, they still can poll it to see those
updates).
We don't need that for now, but that may happen in the future (to
let the subscriber know we don't accept follow on that resource, or because
some fields we decided are mandatory are missing, or to blacklist some
users, or what have you). So I decided to add that value right now, since
it is an enum. It gives us denied = 0
, requested = 1
, accepted = 2
so
we can easily do things like WHERE status > 0
or WHERE status < 2
. If
we don't add denied
now, we would need to rewrite all values in a
possibly massive table later to allow the same thing (because we would have
requested = 0
and accepted = 1
).
queries and query plans
Query and query plan for creating the ActivityPub::ReleasesSubscription
record:
INSERT INTO "activity_pub_releases_subscriptions"
("subscriber_url", "status", "project_id",
"payload", "created_at", "updated_at")
VALUES
('http://127.0.0.1:3001/users/admin', 1, 7,
'{"id":"http://localhost:3001/ef63d04e-e1a8-42c1-a119-327128d1fcca","type":"Follow","actor":"http://localhost:3001/users/admin","object":"http://127.0.0.1:3000/flightjs/Flight/-/releases","@context":"https://www.w3.org/ns/activitystreams"}', '2023-09-27 16:32:43.200625', '2023-09-27 16:32:43.200625')
RETURNING "id"
Insert on activity_pub_releases_subscriptions (cost=0.00..0.01 rows=1 width=130)
-> Result (cost=0.00..0.01 rows=1 width=130)
For ActivityPub::ReleasesSubscription#find_by_subscriber_url
:
SELECT "activity_pub_releases_subscriptions".*
FROM "activity_pub_releases_subscriptions"
WHERE "activity_pub_releases_subscriptions"."subscriber_url" = 'http://127.0.0.1:3001/users/admin'
LIMIT 1
Limit (cost=0.15..1.50 rows=1 width=130)
-> Index Scan using index_activity_pub_releases_subscriptions_on_subscriber_url on activity_pub_releases_subscriptions (cost=0.15..4.20 rows=3 width=130)
Index Cond: (subscriber_url = 'http://127.0.0.1:3001/users/admin'::text)
For updating the subscriber_inbox_url
field:
UPDATE "activity_pub_releases_subscriptions"
SET
"subscriber_inbox_url" = 'http://127.0.0.1:3001/users/admin/inbox',
"updated_at" = '2023-09-27 16:27:56.222183'
WHERE "activity_pub_releases_subscriptions"."id" = 6
Update on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using activity_pub_releases_subscriptions_pkey on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=1 width=46)
Index Cond: (id = 6)
For subscription.accepted!
:
UPDATE "activity_pub_releases_subscriptions"
SET
"status" = 2,
"updated_at" = '2023-09-27 16:24:15.007749'
WHERE "activity_pub_releases_subscriptions"."id" = 6
Update on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using activity_pub_releases_subscriptions_pkey on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=1 width=16)
Index Cond: (id = 6)
For subscription.destroy
:
DELETE FROM "activity_pub_releases_subscriptions"
WHERE "activity_pub_releases_subscriptions"."id" = 6
Delete on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using activity_pub_releases_subscriptions_pkey on activity_pub_releases_subscriptions (cost=0.15..2.17 rows=1 width=6)
Index Cond: (id = 6)