arch.md 9.89 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
# Alex' Thoughts 05/10/18

## Pleroma.Web.ActivityPub

This could be a completely independent library.
Maybe it is a good idea to keep separate ActivityStream (vocabulary and schemas) from ActivityPub (protocol and notifications).
I don't know if schema `Notification` should live in any library.
Specs talk about notifications but it isn't an object or something similar.

### Inbox & Outbox current implementation

I thought it could be like "InboxItem" in FunkWhale implementation: http://docs-funkwhale-funkwhale-549-music-federation-documentation.preview.funkwhale.audio/federation/index.html#activity-creation-and-delivery
`InboxItem` table could be a really good idea.
`Inbox` is a term very clear and specific in the ActivityPub specification,
and furthermore, it would be possible to implement an generic "Inbox" get.

However, it seems we don't implement `Inbox` external `get`s or "Outbox" internal `post`s: https://www.w3.org/TR/activitypub/#Overview

Checking how a message is sent from external server to an actor's `Inbox` I discovered we only support:
  * `Create` Activity types for
    * `Article`, `Note` & `Video` Object type
  * `Follow` Activity type.
  * `Accept` (follows?)
  * `Reject` (follows?)
  * `Like`
  * `Announce`
  * `Update`
    * `Person`, `Application`, `Service`, `Organization`
  * `Delete`
  * `Undo`
    * `Announce`, `Follow`, `Block`, `Like`
  * `Block`

We'll have to implement all of them.
This is going to be really hard to make generic or to add hooks to the possible specific application.

Secondly, reading the actor's `Outbox` it seems we are not sending the actor's activity,
we are sending actor's profile:
  * summary
  * name
  * following list
  * etc

ActivityPub specs says:

>>>
 The outbox is discovered through the outbox property of an actor's profile. The outbox MUST be an OrderedCollection.

The outbox stream contains activities the user has published, subject to the ability of the requestor to retrieve the activity (that is, the contents of the outbox are filtered by the permissions of the person reading it). If a user submits a request without Authorization the server should respond with all of the Public posts. This could potentially be all relevant objects published by the user, though the number of available items is left to the discretion of those implementing and deploying the server.

The outbox accepts HTTP POST requests, with behaviour described in Client to Server Interactions.
>>>

So this has to be fixed.

I still don't know if `Notification` in our code would be similar to `InboxItem`,
but a good idea at least for ActivityPub library.

### Pleroma.Web.ActivityPub.Transmogrifier

The code around the `Inbox` implementation is in this module.
I didn't know what this module does but it has a little bit of doc:

> A module to handle coding from internal to wire ActivityPub and back.

It has difficult to understand function names like `fix_object` which doc says:

> Modifies an incoming AP object (mastodon format) to our internal format.

We have to split this module better.
For a generic library we should have any code related to Mastodon or any other app.
Of course, it is going to be very useful to have a mapper from external to internal data.
We have to find a better name.


76 77 78 79 80
# Alex' Thoughts 04/10/18

## Applications and Libraries

What is a Elixir/Erlang application, from elixir docs https://hexdocs.pm/elixir/Application.html:
81 82 83 84
>>>
Applications are the idiomatic way to package software in Erlang/OTP. To get the idea, they are similar to the “library” concept common in other programming languages, but with some additional characteristics.
An application is a component implementing some specific functionality, with a standardized directory structure, configuration, and lifecycle. Applications are loaded, started, and stopped.
>>>
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

We currently have only a single application, Pleroma.
I don't think we need more in the short or medium term.

However, we should split `lib/pleroma` directory in at least two directories:

    * `lib/pleroma` where will live the core of our application
    * `lib/pleroma_web` where will live the phoenix stuff

This is the new convention in Phoenix.
It tries to split the application logic and the web interface.
The web is an interface, but any application could have more than one.
We could add a command line interface or GraphQL API.
If we keep the logic in the web, reusing code will be much harder.

In later steps we could add more directories, and therefore, more decoupling.

So `lib/pleroma` will be a real application.
It will define an `Application` module.
However, `lib/pleroma_web` does not define an `Application` module,
it won't load, start and stop.
This will be done by `Pleroma`,
so `PleromaWeb` should not be called `application`.
In absence of a better terminology, we'll name it "library".

So, `PleromaWeb` will receive HTTP request from clients and other servers and 
it will make calls to `Pleroma` application.
This means `Pleroma` should know nothing about HTTP
and `PleromaWeb` should know nothing about Repo or databases.
The main task `PleromaWeb` will be "translate" Pleroma terminology and send it using HTTP protocol.


# Contexts

It is interesting that currently we are not using contexts, at least, as explained in Phoenix.

The current convention of Phoenix would be:

```
lib
├── pleroma_web
│   ├── channels
│   │   └── user_socket.ex
│   ├── controllers
│   │   ├── page_controller.ex
│   │   └── user_controller.ex
│   ├── endpoint.ex
│   ├── gettext.ex
│   ├── router.ex
│   ├── templates
│   │   ├── layout
│   │   │   └── app.html.eex
│   │   ├── page
│   │   │   └── index.html.eex
│   │   └── user
│   │       ├── edit.html.eex
│   │       ├── form.html.eex
│   │       ├── index.html.eex
│   │       ├── new.html.eex
│   │       └── show.html.eex
│   └── views
│       ├── error_helpers.ex
│       ├── error_view.ex
│       ├── layout_view.ex
│       ├── page_view.ex
│       └── user_view.ex
└── pleroma_web.ex
```

I think current convention in Phoenix is bad and it is an inheritance of Ruby on Rails.
I prefer to create contexts,
very similar to what we are doing the now in:

```
lib/pleroma/web
├── mastodon_api
│   ├── mastodon_api_controller.ex
│   ├── mastodon_api.ex
│   ├── mastodon_socket.ex
│   └── views
│       ├── account_view.ex
│       ├── filter_view.ex
│       ├── list_view.ex
│       ├── mastodon_view.ex
│       └── status_view.ex
├── oauth
│   ├── app.ex
│   ├── authorization.ex
│   ├── fallback_controller.ex
│   ├── oauth_controller.ex
│   ├── oauth_view.ex
│   └── token.ex
├── ostatus
│   ├── activity_representer.ex
│   ├── feed_representer.ex
│   ├── handlers
│   │   ├── delete_handler.ex
│   │   ├── follow_handler.ex
│   │   ├── note_handler.ex
│   │   └── unfollow_handler.ex
│   ├── ostatus_controller.ex
│   ├── ostatus.ex
│   └── user_representer.ex
├── router.ex
```

Keeping this in mind, it is very interesting to read the (Phoenix documentation about this)[https://hexdocs.pm/phoenix/contexts.html#content]

The main point is not creating too many applications or libraries just to separates concerns or application logic.
A context has a public API which should be used by other modules or applications, ie:

  * `Pleroma.Actors` is a context main module and it is where the public API.
  * `Pleroma.Actors.create(params)`
  * `Pleroma.Actors.find_by(params)`
  * `Pleroma.Actors.send_notification(actor, notification)`

This context could have internal modules, ie:
  * `Pleroma.Actors.Actor # Schema`
  * `Pleroma.Actors.Finders # Queries`
  * `Pleroma.Actors.CreateCommand # Logic to create an Actor`

Those internal modules should not be used directly by an external contexts and they should not have documentation.
Contexts can talk each other.
In fact different libraries could share context name, ie:
  * When `PleromaWeb.Actors.Controller` receives a new registration request and it will call `Pleroma.Actors.create_actor(params)`

However, sometimes we need to use different context in the same time.
Imagine we add new context: `Pleroma.Statistics`
So anytime a user is created we should call: `Pleroma.Statistics.new_registration(actor)`
If we make this call from `PleromaWeb.Actors.Controller` we can forget to add it in a future interface.
In this case, the appropriate way is to create a new function in the application level:

```
defmodule Pleroma do
  def create_actor(params)
     with {:ok, actor} <- Pleroma.Actors.create(params),
             :ok <- `Pleroma.Statistics.new_registration(actor) do
        {:ok, actor}
     end
  end
end
```

The `Pleroma` application is in charge of making the call between different contexts.

This could turn in a mess very quickly.
Too many big functions in the `Pleroma` module, which is the Public API for the application.

Well, we can just create helper modules like: `Pleroma.CreateActor`.
We can move this function and private helpers functions to this module.
So the only responsibility is to create the actor.
And of course it will be a private module:

```
defmodule Pleroma do
  @doc """wherever"""
  def create_actors(params), do: Pleroma.CreateActor.run(params)
end

defmodule Pleroma.CreateActor do
  @moduledoc false

  def run(params) do
     ...
  end
end
```

This means no documentation at all.
The doc should be in the Contexts (if they are publics) or in `Pleroma` module.

### Contexts for Pleroma

* `Pleroma.Invitations` deals with Invitations
* `Pleroma.ActivityStream` has `Activity`, `Actor`, etc. schemas
* Think about if it makes sense to split `ActivityPub` and `ActivityStream`