Skip to content

Commit 10daf51

Browse files
committed
chore: fix up problems with follower creation
1 parent 846ef4c commit 10daf51

File tree

6 files changed

+423
-41
lines changed

6 files changed

+423
-41
lines changed

bases/renku_data_services/data_api/app.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,11 @@ def register_all_handlers(app: Sanic, config: Config) -> Sanic:
231231
)
232232

233233
# ActivityPub configuration
234+
# Use the correct base URL for the local development environment
235+
base_url = "http://localhost:8000"
234236
activitypub_config = ActivityPubConfig(
235237
domain=config.domain,
236-
base_url=config.base_url,
238+
base_url=f"{base_url}{url_prefix}", # Use the correct base URL with API prefix
237239
admin_email=config.admin_email,
238240
)
239241

components/renku_data_services/activitypub/api.spec.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,37 @@ paths:
7373
application/json:
7474
schema:
7575
$ref: '#/components/schemas/Error'
76+
/ap/projects/{project_id}/followers/{follower_uri}:
77+
delete:
78+
summary: Remove a follower from a project
79+
description: Removes a follower from a project's followers list
80+
operationId: removeProjectFollower
81+
tags:
82+
- ActivityPub
83+
parameters:
84+
- name: project_id
85+
in: path
86+
description: ID of the project
87+
required: true
88+
schema:
89+
type: string
90+
format: ulid
91+
- name: follower_uri
92+
in: path
93+
description: URI of the follower to remove
94+
required: true
95+
schema:
96+
type: string
97+
format: uri
98+
responses:
99+
'204':
100+
description: Follower removed successfully
101+
'404':
102+
description: Project or follower not found
103+
content:
104+
application/json:
105+
schema:
106+
$ref: '#/components/schemas/Error'
76107
/ap/projects/{project_id}/inbox:
77108
post:
78109
summary: Receive an ActivityPub activity for a project

components/renku_data_services/activitypub/blueprints.py

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import json
44
import logging
5+
import urllib.parse
56
from dataclasses import dataclass
67
from typing import Any, Dict, List, Optional, Union
78
from urllib.parse import urlparse
@@ -66,6 +67,30 @@ async def _get_project_followers(request: Request, user: base_models.APIUser, pr
6667

6768
return "/ap/projects/<project_id:ulid>/followers", ["GET"], _get_project_followers
6869

70+
def remove_project_follower(self) -> BlueprintFactoryResponse:
71+
"""Remove a follower from a project."""
72+
73+
@authenticate(self.authenticator)
74+
async def _remove_project_follower(
75+
request: Request, user: base_models.APIUser, project_id: ULID, follower_uri: str
76+
) -> JSONResponse:
77+
try:
78+
# URL-decode the follower_uri
79+
follower_uri = urllib.parse.unquote(follower_uri)
80+
81+
# Remove the follower
82+
await self.activitypub_service.handle_unfollow(user=user, project_id=project_id, follower_actor_uri=follower_uri)
83+
84+
# Return a 204 No Content response
85+
return JSONResponse(None, status=204)
86+
except errors.MissingResourceError as e:
87+
return JSONResponse(
88+
{"error": "not_found", "message": str(e)},
89+
status=404,
90+
)
91+
92+
return "/ap/projects/<project_id:ulid>/followers/<follower_uri:path>", ["DELETE"], _remove_project_follower
93+
6994
def project_inbox(self) -> BlueprintFactoryResponse:
7095
"""Receive an ActivityPub activity for a project."""
7196

@@ -90,13 +115,20 @@ async def _project_inbox(request: Request, project_id: ULID) -> HTTPResponse:
90115
status=400,
91116
)
92117

93-
# Handle the follow request
94-
# Note: We're using an admin user here because we need to access the project regardless of permissions
95-
admin_user = base_models.APIUser(id=None, is_admin=True)
96-
await self.activitypub_service.handle_follow(
97-
user=admin_user, project_id=project_id, follower_actor_uri=actor_uri
98-
)
99-
return HTTPResponse(status=200)
118+
try:
119+
# Handle the follow request
120+
# Note: We're using an admin user here because we need to access the project regardless of permissions
121+
admin_user = base_models.APIUser(id=None, is_admin=True)
122+
await self.activitypub_service.handle_follow(
123+
user=admin_user, project_id=project_id, follower_actor_uri=actor_uri
124+
)
125+
return HTTPResponse(status=200)
126+
except Exception as e:
127+
logger.exception(f"Error handling follow activity: {e}")
128+
return JSONResponse(
129+
{"error": "internal_error", "message": f"Error handling follow: {str(e)}"},
130+
status=500,
131+
)
100132
elif activity_type == models.ActivityType.UNDO:
101133
# Check if the object is a Follow activity
102134
object_json = activity_json.get("object", {})
@@ -109,13 +141,20 @@ async def _project_inbox(request: Request, project_id: ULID) -> HTTPResponse:
109141
status=400,
110142
)
111143

112-
# Handle the unfollow request
113-
# Note: We're using an admin user here because we need to access the project regardless of permissions
114-
admin_user = base_models.APIUser(id=None, is_admin=True)
115-
await self.activitypub_service.handle_unfollow(
116-
user=admin_user, project_id=project_id, follower_actor_uri=actor_uri
117-
)
118-
return HTTPResponse(status=200)
144+
try:
145+
# Handle the unfollow request
146+
# Note: We're using an admin user here because we need to access the project regardless of permissions
147+
admin_user = base_models.APIUser(id=None, is_admin=True)
148+
await self.activitypub_service.handle_unfollow(
149+
user=admin_user, project_id=project_id, follower_actor_uri=actor_uri
150+
)
151+
return HTTPResponse(status=200)
152+
except Exception as e:
153+
logger.exception(f"Error handling unfollow activity: {e}")
154+
return JSONResponse(
155+
{"error": "internal_error", "message": f"Error handling unfollow: {str(e)}"},
156+
status=500,
157+
)
119158

120159
# For other activity types, just acknowledge receipt
121160
return HTTPResponse(status=200)
@@ -127,7 +166,7 @@ async def _project_inbox(request: Request, project_id: ULID) -> HTTPResponse:
127166
except Exception as e:
128167
logger.exception(f"Error handling activity: {e}")
129168
return JSONResponse(
130-
{"error": "internal_error", "message": "An internal error occurred"},
169+
{"error": "internal_error", "message": f"An internal error occurred: {str(e)}"},
131170
status=500,
132171
)
133172

0 commit comments

Comments
 (0)