mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 13:16:40 +00:00
153 lines
5 KiB
Markdown
153 lines
5 KiB
Markdown
# streams
|
|
|
|
ActivityStreams vocabularies automatically code-generated with `astool`.
|
|
|
|
## Reference & Tutorial
|
|
|
|
The [go-fed website](https://go-fed.org/) 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:
|
|
|
|
```golang
|
|
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:
|
|
|
|
```golang
|
|
// 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.
|
|
|
|
```golang
|
|
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:
|
|
|
|
```golang
|
|
// 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.
|