mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 17:42:25 -06: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.
							 |