diff --git a/.build.yml b/.build.yml index 6839cc5..c5e57f1 100644 --- a/.build.yml +++ b/.build.yml @@ -1,6 +1,4 @@ image: archlinux -secrets: -- 72c9ab0b-ed5f-4291-bab7-30b361be94a6 packages: - go sources: @@ -15,10 +13,3 @@ tasks: - coverage: | set -a +x cd activitypub && make coverage - GIT_SHA=$(git rev-parse --verify HEAD) - GIT_BRANCH=$(git name-rev --name-only HEAD) - source ~/.code-cov.sh - curl -X POST \ - --data-binary @activitypub.coverprofile \ - -H 'Accept: application/json' \ - "https://codecov.io/upload/v2?commit=${GIT_SHA}&token=${PUB_TOKEN}&branch=${GIT_BRANCH}&service=custom" || true diff --git a/go.mod b/go.mod index 1d35a58..c9dccfa 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 - github.com/go-ap/errors v0.0.0-20220917143055-4283ea5dae18 - github.com/go-ap/jsonld v0.0.0-20220917142617-76bf51585778 + github.com/go-ap/errors v0.0.0-20221115052505-8aaa26f930b4 + github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/valyala/fastjson v1.6.3 ) diff --git a/iri.go b/iri.go index b8994c8..c52994a 100644 --- a/iri.go +++ b/iri.go @@ -6,7 +6,7 @@ import ( "fmt" "io" "net/url" - "path" + "path/filepath" "strings" "github.com/valyala/fastjson" @@ -134,7 +134,8 @@ func (i *IRIs) GobDecode(data []byte) error { // AddPath concatenates el elements as a path to i func (i IRI) AddPath(el ...string) IRI { - return IRI(fmt.Sprintf("%s/%s", i, path.Join(el...))) + iri := strings.TrimRight(i.String(), "/") + return IRI(iri + filepath.Clean(filepath.Join("/", filepath.Join(el...)))) } // GetID @@ -215,6 +216,55 @@ func (i *IRIs) UnmarshalJSON(data []byte) error { return nil } +// GetID returns the ID corresponding to ItemCollection +func (i IRIs) GetID() ID { + return EmptyID +} + +// GetLink returns the empty IRI +func (i IRIs) GetLink() IRI { + return EmptyIRI +} + +// GetType returns the ItemCollection's type +func (i IRIs) GetType() ActivityVocabularyType { + return CollectionOfItems +} + +// IsLink returns false for an ItemCollection object +func (i IRIs) IsLink() bool { + return false +} + +// IsObject returns true for a ItemCollection object +func (i IRIs) IsObject() bool { + return false +} + +// IsCollection returns true for IRI slices +func (i IRIs) IsCollection() bool { + return true +} + +// Append facilitates adding elements to IRI slices +// and ensures IRIs implements the Collection interface +func (i *IRIs) Append(r IRI) error { + *i = append(*i, r) + return nil +} + +func (i *IRIs) Collection() ItemCollection { + res := make(ItemCollection, len(*i)) + for k, iri := range *i { + res[k] = iri + } + return res +} + +func (i *IRIs) Count() uint { + return uint(len(*i)) +} + // Contains verifies if IRIs array contains the received one func (i IRIs) Contains(r IRI) bool { if len(i) == 0 { @@ -250,7 +300,7 @@ func (i IRI) Equals(with IRI, checkScheme bool) bool { if strings.ToLower(u.Host) != strings.ToLower(uw.Host) { return false } - if path.Clean(u.Path) != path.Clean(uw.Path) { + if filepath.Clean(u.Path) != filepath.Clean(uw.Path) { return false } uq := u.Query() @@ -311,11 +361,11 @@ func (i IRI) Contains(what IRI, checkScheme bool) bool { } p := u.Path if p != "" { - p = path.Clean(p) + p = filepath.Clean(p) } pw := uw.Path if pw != "" { - pw = path.Clean(pw) + pw = filepath.Clean(pw) } return strings.Contains(p, pw) } diff --git a/item.go b/item.go index 7d8405a..567bcb4 100644 --- a/item.go +++ b/item.go @@ -146,7 +146,7 @@ func IsNil(it Item) bool { // NOTE(marius): we're not dealing with a type that we know about, so we use slow reflection // as we still care about the result v := reflect.ValueOf(it) - isNil = v.Kind() != reflect.Pointer || v.IsNil() + isNil = v.Kind() == reflect.Pointer && v.IsNil() } return isNil } diff --git a/natural_language_values.go b/natural_language_values.go index defe0e0..6a0c17d 100644 --- a/natural_language_values.go +++ b/natural_language_values.go @@ -15,6 +15,9 @@ import ( // It is used for LangRefValue objects without an explicit language key. const NilLangRef LangRef = "-" +// DefaultLang represents the default language reference used when using the convenience content generation. +var DefaultLang = NilLangRef + type ( // LangRef is the type for a language reference code, should be an ISO639-1 language specifier. LangRef string @@ -29,8 +32,16 @@ type ( NaturalLanguageValues []LangRefValue ) -func NaturalLanguageValuesNew() NaturalLanguageValues { - return make(NaturalLanguageValues, 0) +func NaturalLanguageValuesNew(values ...LangRefValue) NaturalLanguageValues { + n := make(NaturalLanguageValues, len(values)) + for i, val := range values { + n[i] = val + } + return n +} + +func DefaultNaturalLanguageValue(content string) NaturalLanguageValues { + return NaturalLanguageValuesNew(DefaultLangRef(content)) } func (n NaturalLanguageValues) String() string { @@ -74,6 +85,10 @@ func (n *NaturalLanguageValues) Set(ref LangRef, v Content) error { return nil } +func (n *NaturalLanguageValues) Add(ref LangRefValue) { + *n = append(*n, ref) +} + // MarshalJSON encodes the receiver object to a JSON document. func (n NaturalLanguageValues) MarshalJSON() ([]byte, error) { l := len(n) @@ -183,6 +198,14 @@ func (l LangRefValue) String() string { return fmt.Sprintf("%s[%s]", l.Value, l.Ref) } +func DefaultLangRef(value string) LangRefValue { + return LangRefValue{Ref: DefaultLang, Value: Content(value)} +} + +func LangRefValueNew(lang LangRef, value string) LangRefValue { + return LangRefValue{Ref: lang, Value: Content(value)} +} + func (l LangRefValue) Format(s fmt.State, verb rune) { switch verb { case 's', 'q': diff --git a/typer.go b/typer.go index 6df8a3d..857014c 100644 --- a/typer.go +++ b/typer.go @@ -2,7 +2,7 @@ package activitypub import ( "fmt" - "path" + "path/filepath" "strings" "github.com/go-ap/errors" @@ -71,7 +71,7 @@ func (t CollectionPaths) Contains(typ CollectionPath) bool { // Split splits the IRI in an actor IRI and its CollectionPath // if the CollectionPath is found in the elements in the t CollectionPaths slice func (t CollectionPaths) Split(i IRI) (IRI, CollectionPath) { - maybeActor, maybeCol := path.Split(i.String()) + maybeActor, maybeCol := filepath.Split(i.String()) tt := CollectionPath(maybeCol) if !t.Contains(tt) { tt = "" @@ -104,38 +104,84 @@ func (t CollectionPath) IRI(i Item) IRI { return IRIf(i.GetLink(), t) } -// Of gives us the property of the i Item that corresponds to the t CollectionPath type. -func (t CollectionPath) Of(i Item) Item { - if IsNil(i) || !i.IsObject() { +func (t CollectionPath) ofItemCollection(col ItemCollection) Item { + iriCol := make(ItemCollection, len(col)) + for i, it := range col { + iriCol[i] = t.Of(it) + } + return iriCol +} + +func (t CollectionPath) ofObject(ob *Object) Item { + var it Item + switch t { + case Likes: + it = ob.Likes + case Shares: + it = ob.Shares + case Replies: + it = ob.Replies + } + if it == nil { + it = t.ofIRI(ob.ID) + } + return it +} +func (t CollectionPath) ofActor(a *Actor) Item { + var it Item + switch t { + case Inbox: + it = a.Inbox + case Outbox: + it = a.Outbox + case Liked: + it = a.Liked + case Following: + it = a.Following + case Followers: + it = a.Followers + } + if it == nil { + it = t.ofIRI(a.ID) + } + return it +} + +func (t CollectionPath) ofIRI(iri IRI) Item { + if len(iri) == 0 { return nil } + return iri.AddPath(string(t)) +} + +func (t CollectionPath) ofItem(i Item) Item { var it Item + return it +} + +// Of gives us the property of the i Item that corresponds to the t CollectionPath type. +func (t CollectionPath) Of(i Item) Item { + if IsNil(i) { + return nil + } + if IsIRI(i) { + return t.ofIRI(i.GetLink()) + } + var it Item + if IsItemCollection(i) { + OnItemCollection(i, func(col *ItemCollection) error { + it = t.ofItemCollection(*col) + return nil + }) + } if OfActor.Contains(t) && ActorTypes.Contains(i.GetType()) { OnActor(i, func(a *Actor) error { - switch t { - case Inbox: - it = a.Inbox - case Outbox: - it = a.Outbox - case Liked: - it = a.Liked - case Following: - it = a.Following - case Followers: - it = a.Followers - } + it = t.ofActor(a) return nil }) } OnObject(i, func(o *Object) error { - switch t { - case Likes: - it = o.Likes - case Shares: - it = o.Shares - case Replies: - it = o.Replies - } + it = t.ofObject(o) return nil }) @@ -144,7 +190,7 @@ func (t CollectionPath) Of(i Item) Item { // OfActor returns the base IRI of received i, if i represents an IRI matching CollectionPath type t func (t CollectionPath) OfActor(i IRI) (IRI, error) { - maybeActor, maybeCol := path.Split(i.String()) + maybeActor, maybeCol := filepath.Split(i.String()) if strings.ToLower(maybeCol) == strings.ToLower(string(t)) { maybeActor = strings.TrimRight(maybeActor, "/") return IRI(maybeActor), nil