Add project activities actor

Why are we doing this work

We should have a well oiled ActivityPub stack, by now. For the sack of completeness, this is about adding a project activities actor, to publish through ActivityPub the same kind of activities that we can find on the project activities tab.

While only useful for most hardcore users, who are really interested in a given project, it will also add the entities that will be used by the user actor, since following a user is following their activities on all resources, including projects.

Profile

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": PROJECT_URL,
  "type": "Application",
  "name": PROJECT_NAME,
  "summary": PROJECT_DESCRIPTION,
  "url": PROJECT_URL,
  "outbox": PROJECT_OUTBOX_URL,
  "inbox": null,
}

Outbox

For project, we can map the events happening on the project activity timeline on Gitlab, when a user:

  • created the repository
  • pushed commits
  • pushed a tag
  • opened a merge request
  • accepted a merge request
  • closed a merge request
  • opened an issue
  • closed an issue
  • reopened an issue
  • commented on a merge request
  • commented on an issue
  • created a wiki page
  • updated a wiki page
  • destroyed a wiki page
  • joined the project
  • left the project
  • deleted the project

There's also a Design tab in the project activities, but it's just empty in all projects I follow and I don't see anything related to it in my projects sidebar. Maybe it's a premium feature? If so, it's of no concern to us for public following through ActivityPub.

user created the repository

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Create",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  }
}

User pushed commits

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Update",
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
  "result": COMMITS_DIFF_URL,
}

User pushed a tag

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Update",
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
  "name": TAG_NAME,
  "result": COMMIT_URL,
}

User opened a merge request

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Add",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": MERGE_REQUEST_URL,
    "type": "Application",
    "name": MERGE_REQUEST_TITLE,
    "url": MERGE_REQUEST_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
  "target": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

User accepted a merge request

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Accept",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": MERGE_REQUEST_URL,
    "type": "Application",
    "name": MERGE_REQUEST_TITLE,
    "url": MERGE_REQUEST_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
  "target": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

User closed a merge request

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Remove",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": MERGE_REQUEST_URL,
    "type": "Application",
    "name": MERGE_REQUEST_TITLE,
    "url": MERGE_REQUEST_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
  "origin": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

User opened an issue

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Add",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": ISSUE_URL,
    "type": "Page",
    "name": ISSUE_TITLE,
    "url": ISSUE_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    }
  },
  "target": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  }
}

Why to add the project both as object.context and target? For multiple reasons about consistency:

  • The Add activity is more commonly used with a target.
  • … and the Remove activity used below to close the issue is more commonly used with a origin.
  • … but the Update activity used to reopen an issue specifies that target and origin have no specific meaning, making context better suited for that.
  • So we could go with using context only with Update, … but then there are merge requests.
  • Merge requests are very similar to issues, so we want their activities to be similar.
  • While the best type for issues is Page, the type chosen for merge request is Application, both to distinguish it from issues and because they contain code.
  • To distinguish merge requests from projects (which are Application too), merge requests are Application having an other Application as context (the project).
  • Given the merge request will have a context even with the Add and Remove activities, the same is done with issues, for consistency.

An alternative would have been to not use Add for issues and merge requests, but instead Create : that way, we can always use context. But it creates more problems that it solves. Accept and Reject would work quite well for closing merge requests, but what do we use for closing issues? Delete would feel weird, as the issue is not deleted, it's just closed. Especially if we reopen the issue later, it would be an Update after a Delete. And if we used Create for opening issues and Remove for closing issue, it would feel asymmetrical and tricky (Create is mirrored by Delete, and Add is mirrored by Remove). To minimize pain for those who will build on top of those resources, it's best to duplicate the project information as context and target / origin.

User closed an issue

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Remove",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": ISSUE_URL,
    "type": "Page",
    "name": ISSUE_TITLE,
    "url": ISSUE_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
  "origin": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

User reopened an issue

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Update",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": ISSUE_URL,
    "type": "Page",
    "name": ISSUE_TITLE,
    "url": ISSUE_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
}

User commented on a merge request

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Add",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": NOTE_URL,
    "type": "Note",
    "content": NOTE_NOTE,
  },
  "target": {
    "id": MERGE_REQUEST_URL,
    "type": "Application",
    "name": MERGE_REQUEST_TITLE,
    "url": MERGE_REQUEST_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
}

User commented on an issue

{
  "id": PROJECT_URL#event_id,
  "type": "Add",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": NOTE_URL,
    "type": "Note",
    "content": NOTE_NOTE,
  },
  "target": {
    "id": ISSUE_URL,
    "type": "Page",
    "name": ISSUE_TITLE,
    "url": ISSUE_URL,
    "context": {
      "id": PROJECT_URL,
      "type": "Application",
      "name": PROJECT_NAME,
      "summary": PROJECT_DESCRIPTION,
      "url": PROJECT_URL,
    },
  },
}

User created a wiki page

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Create",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": WIKI_PAGE_URL,
    "type": "Page",
    "name": WIKI_PAGE_HUMAN_TITLE,
    "url": WIKI_PAGE_URL,
  }
}

User updated a wiki page

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Update",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": WIKI_PAGE_URL,
    "type": "Page",
    "name": WIKI_PAGE_HUMAN_TITLE,
    "url": WIKI_PAGE_URL,
  }
}

User destroyed a wiki page

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Delete",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": WIKI_PAGE_URL,
    "type": "Page",
    "name": WIKI_PAGE_HUMAN_TITLE,
    "url": WIKI_PAGE_URL,
  }
}

User joined the project

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Add",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "target": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

Note: Gitlab's project timeline does not mention who added a member to the project, so we'll do the same. Still, the Add activity requires an Actor. For that reason, we'll just use the same person as actor and object.

In the members page of a project, there is actually a "source" attribute. While there is sometimes mention of who added the user, this is used mainly to distinguish if the user is a member attached to the project directly, or through a group, so it would not be a good "actor" (that would rather be an "origin" for the membership).

User left the project

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Remove",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "target": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  },
}

See comment in User joined the project.

user deleted the repository

{
  "id": PROJECT_OUTBOX_URL#event_id,
  "type": "Delete",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "actor": {
    "id": USER_PROFILE_URL,
    "type": "Person",
    "name": USER_NAME,
    "url": USER_PROFILE_URL,
  },
  "object": {
    "id": PROJECT_URL,
    "type": "Application",
    "name": PROJECT_NAME,
    "summary": PROJECT_DESCRIPTION,
    "url": PROJECT_URL,
  }
}

Relevant links

Non-functional requirements

  • Documentation:
  • Testing:

Implementation plan

  • actor's profile endpoint
  • actor's outbox endpoint
    • created the repository
    • pushed commits
    • pushed a tag
    • opened a merge request
    • accepted a merge request
    • closed a merge request
    • opened an issue
    • closed an issue
    • reopened an issue
    • commented on a merge request
    • commented on an issue
    • created a wiki page
    • updated a wiki page
    • destroyed a wiki page
    • joined the project
    • left the project
    • deleted the project
  • emit ActivityPub push requests
    • created the repository
    • pushed commits
    • pushed a tag
    • opened a merge request
    • accepted a merge request
    • closed a merge request
    • opened an issue
    • closed an issue
    • reopened an issue
    • commented on a merge request
    • commented on an issue
    • created a wiki page
    • updated a wiki page
    • destroyed a wiki page
    • joined the project
    • left the project
    • deleted the project

Verification steps

TBD

Edited by kik