~~Still WIP!~~ This PR allows v0.20.0 of GtS to be forward-compatible with the interaction request / authorization flow that will fully replace the current flow in v0.21.0. Basically, this means we need to recognize LikeRequest, ReplyRequest, and AnnounceRequest, and in response to those requests, deliver either a Reject or an Accept, with the latter pointing towards a LikeAuthorization, ReplyAuthorization, or AnnounceAuthorization, respectively. This can then be used by the remote instance to prove to third parties that the interaction has been accepted by the interactee. These Authorization types need to be dereferencable to third parties, so we need to serve them. As well as recognizing the above "polite" interaction request types, we also need to still serve appropriate responses to "impolite" interaction request types, where an instance that's unaware of interaction policies tries to interact with a post by sending a reply, like, or boost directly, without wrapping it in a WhateverRequest type. Doesn't fully close https://codeberg.org/superseriousbusiness/gotosocial/issues/4026 but gets damn near (just gotta update the federating with GtS documentation). Migrations tested on both Postgres and SQLite. Co-authored-by: kim <grufwub@gmail.com> Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4394 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com> |
||
|---|---|---|
| .. | ||
| impl | ||
| values | ||
| vocab | ||
| gen_consts.go | ||
| gen_doc.go | ||
| gen_init.go | ||
| gen_json_resolver.go | ||
| gen_manager.go | ||
| gen_pkg_activitystreams_disjoint.go | ||
| gen_pkg_activitystreams_extendedby.go | ||
| gen_pkg_activitystreams_extends.go | ||
| gen_pkg_activitystreams_isorextends.go | ||
| gen_pkg_activitystreams_property_constructors.go | ||
| gen_pkg_activitystreams_type_constructors.go | ||
| gen_pkg_funkwhale_disjoint.go | ||
| gen_pkg_funkwhale_extendedby.go | ||
| gen_pkg_funkwhale_extends.go | ||
| gen_pkg_funkwhale_isorextends.go | ||
| gen_pkg_funkwhale_type_constructors.go | ||
| gen_pkg_gotosocial_disjoint.go | ||
| gen_pkg_gotosocial_extendedby.go | ||
| gen_pkg_gotosocial_extends.go | ||
| gen_pkg_gotosocial_isorextends.go | ||
| gen_pkg_gotosocial_property_constructors.go | ||
| gen_pkg_gotosocial_type_constructors.go | ||
| gen_pkg_jsonld_property_constructors.go | ||
| gen_pkg_litepub_disjoint.go | ||
| gen_pkg_litepub_extendedby.go | ||
| gen_pkg_litepub_extends.go | ||
| gen_pkg_litepub_isorextends.go | ||
| gen_pkg_litepub_type_constructors.go | ||
| gen_pkg_schema_disjoint.go | ||
| gen_pkg_schema_extendedby.go | ||
| gen_pkg_schema_extends.go | ||
| gen_pkg_schema_isorextends.go | ||
| gen_pkg_schema_property_constructors.go | ||
| gen_pkg_schema_type_constructors.go | ||
| gen_pkg_toot_disjoint.go | ||
| gen_pkg_toot_extendedby.go | ||
| gen_pkg_toot_extends.go | ||
| gen_pkg_toot_isorextends.go | ||
| gen_pkg_toot_property_constructors.go | ||
| gen_pkg_toot_type_constructors.go | ||
| gen_pkg_w3idsecurityv1_disjoint.go | ||
| gen_pkg_w3idsecurityv1_extendedby.go | ||
| gen_pkg_w3idsecurityv1_extends.go | ||
| gen_pkg_w3idsecurityv1_isorextends.go | ||
| gen_pkg_w3idsecurityv1_property_constructors.go | ||
| gen_pkg_w3idsecurityv1_type_constructors.go | ||
| gen_resolver_utils.go | ||
| gen_type_predicated_resolver.go | ||
| gen_type_resolver.go | ||
| README.md | ||
| util.go | ||
streams
ActivityStreams vocabularies automatically code-generated with astool.
Reference & Tutorial
The go-fed website contains tutorials and reference materials, in addition to the rest of this README.
How To Use
go get github.com/go-fed/activity
All generated types and properties are interfaces in
github.com/go-fed/streams/vocab, but note that the constructors and supporting
functions live in github.com/go-fed/streams.
To create a type and set properties:
var actorURL *url.URL = // ...
// A new "Create" Activity.
create := streams.NewActivityStreamsCreate()
// A new "actor" property.
actor := streams.NewActivityStreamsActorProperty()
actor.AppendIRI(actorURL)
// Set the "actor" property on the "Create" Activity.
create.SetActivityStreamsActor(actor)
To process properties on a type:
// Returns true if the "Update" has at least one "object" with an IRI value.
func hasObjectWithIRIValue(update vocab.ActivityStreamsUpdate) bool {
objectProperty := update.GetActivityStreamsObject()
// Any property may be nil if it was either empty in the original JSON or
// never set on the golang type.
if objectProperty == nil {
return false
}
// The "object" property is non-functional: it could have multiple values. The
// generated code has slightly different methods for a functional property
// versus a non-functional one.
//
// While it may be easy to ignore multiple values in other languages
// (accidentally or purposefully), go-fed is designed to make it hard to do
// so.
for iter := objectProperty.Begin(); iter != objectProperty.End(); iter = iter.Next() {
// If this particular value is an IRI, return true.
if iter.IsIRI() {
return true
}
}
// All values are literal embedded values and not IRIs.
return false
}
The ActivityStreams type hierarchy of "extends" and "disjoint" is not the same as the Object Oriented definition of inheritance. It is also not the same as golang's interface duck-typing. Helper functions are provided to guarantee that an application's logic can correctly apply the type hierarchy.
thing := // Pick a type from streams.NewActivityStreams<Type>()
if streams.ActivityStreamsObjectIsDisjointWith(thing) {
fmt.Printf("The \"Object\" type is Disjoint with the %T type.\n", thing)
}
if streams.ActivityStreamsLinkIsExtendedBy(thing) {
fmt.Printf("The %T type Extends from the \"Link\" type.\n", thing)
}
if streams.ActivityStreamsActivityExtends(thing) {
fmt.Printf("The \"Activity\" type extends from the %T type.\n", thing)
}
When given a generic JSON payload, it can be resolved to a concrete type by
creating a streams.JSONResolver and giving it a callback function that accepts
the interesting concrete type:
// Callbacks must be in the form:
// func(context.Context, <TypeInterface>) error
createCallback := func(c context.Context, create vocab.ActivityStreamsCreate) error {
// Do something with 'create'
fmt.Printf("createCallback called: %T\n", create)
return nil
}
updateCallback := func(c context.Context, update vocab.ActivityStreamsUpdate) error {
// Do something with 'update'
fmt.Printf("updateCallback called: %T\n", update)
return nil
}
jsonResolver, err := streams.NewJSONResolver(createCallback, updateCallback)
if err != nil {
// Something in the setup was wrong. For example, a callback has an
// unsupported signature and would never be called
panic(err)
}
// Create a context, which allows you to pass data opaquely through the
// JSONResolver.
c := context.Background()
// Example 15 of the ActivityStreams specification.
b := []byte(`{
"@context": "https://www.w3.org/ns/activitystreams",
"summary": "Sally created a note",
"type": "Create",
"actor": {
"type": "Person",
"name": "Sally"
},
"object": {
"type": "Note",
"name": "A Simple Note",
"content": "This is a simple note"
}
}`)
var jsonMap map[string]interface{}
if err = json.Unmarshal(b, &jsonMap); err != nil {
panic(err)
}
// The createCallback function will be called.
err = jsonResolver.Resolve(c, jsonMap)
if err != nil && !streams.IsUnmatchedErr(err) {
// Something went wrong
panic(err)
} else if streams.IsUnmatchedErr(err) {
// Everything went right but the callback didn't match or the ActivityStreams
// type is one that wasn't code generated.
fmt.Println("No match: ", err)
}
A streams.TypeResolver is similar but uses the golang types instead. It
accepts the generic vocab.Type. This is the abstraction when needing to handle
any ActivityStreams type. The function ToType can convert a JSON-decoded-map
into this kind of value if needed.
A streams.PredicatedTypeResolver lets you apply a boolean predicate function
that acts as a check whether a callback is allowed to be invoked.
FAQ
Why Are Empty Properties Nil And Not Zero-Valued?
Due to implementation design decisions, it would require a lot of plumbing to ensure this would work properly. It would also require allocation of a non-trivial amount of memory.