go-fed / activity

ActivityStreams & ActivityPub in golang, oh my!
BSD 3-Clause "New" or "Revised" License
702 stars 111 forks source link

ActivityStreamsOrderedProperty isn't array with only one item #139

Open Sigafoos opened 3 years ago

Sigafoos commented 3 years ago

Please Describe The Problematic Behavior I have this code:

    followers := streams.NewActivityStreamsOrderedCollection()

    id := streams.NewJSONLDIdProperty()
    id.SetIRI(u.FollowersIRI())

    items := streams.NewActivityStreamsOrderedItemsProperty()
    for _, follower := range u.Followers {
        log.Println("appending follower", follower.ID)
        iri, err := url.Parse(follower.ID)
        if err != nil {
            log.Println("error parsing url for followers list:", err.Error())
            continue
        }
        items.PrependIRI(iri)
    }
    followers.SetActivityStreamsOrderedItems(items)

When I hit the followers endpoint of my app, this is the response

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "orderedItems": "http://localhost:8080/user/test",
  "type": "OrderedCollection"
}

I added two calls to PrependIRI() and the response was an array:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "orderedItems": [
    "http://localhost:8080/user/test",
    "http://localhost:8080/user/test"
  ],
  "type": "OrderedCollection"
}

Please Describe The Expected Behavior

When there is only one item in the ordered items it still renders as an array.

cjslep commented 3 years ago

Please Describe The Expected Behavior

When there is only one item in the ordered items it still renders as an array.

Ah, man. So the root issue is that one element is a "string" and more than one is an ["array", "of", "strings"]?

Unfortunately, these are all valid from ActivityStreams' perspective.

Fixing this is not easy. The reason this is not a straightforward fix is because these expectations are quite arbitrary. For example, this is behavior enshrined in the code-generation for non-functional properties, that funny jargon for "properties able to have >1 value".

Any change in that code-generation means it affects all such properties. So things like orderedItems and items make sense to have this change. When there's one item, most will be able to handle "item": ["foo"]. But, other non-functional properties' conventions are not this. Unfortunately, the big elephant in the room is the all-important type property as it is also non-functional, and maybe in more than one meaning. This means such a change would cause a "type": "Create" to be serialized as a "type": ["Create"], and I am willing to bet that would cause numerous insidious federation issues.

So we are caught in a valley where go-fed's rigor means the two quick & easy options are both sub-optimal. Go-fed is in a bit too deep of its own rigor to break away and live the lackadaiscal life.

So what are the options?

  1. Do nothing. No fun for anyone. :(
  2. Make the change. Pray go-fed clients' federation doesn't break peers' software.
  3. If the client is written in Go, change the consuming (client) side to also use the streams vocabulary to be able to handle this properly. Or be a Javascript client :P or other dynamically typed language (joking). The downside is it is more work for all clients involved.
  4. Do a "real fix"

A real "fix" for this could involve: