Finish initial ForgeFed implementation
parent
d12fd434ba
commit
a7f32d3382
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
const (
|
||||
BranchType ap.ActivityVocabularyType = "Branch"
|
||||
)
|
||||
|
||||
type Branch struct {
|
||||
ap.Object
|
||||
// Ref the unique identifier of the branch within the repo
|
||||
Ref ap.Item `jsonld:"ref,omitempty"`
|
||||
}
|
||||
|
||||
// BranchNew initializes a Branch type Object
|
||||
func BranchNew() *Branch {
|
||||
a := ap.ObjectNew(BranchType)
|
||||
o := Branch{Object: *a}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (br Branch) MarshalJSON() ([]byte, error) {
|
||||
b, err := br.Object.MarshalJSON()
|
||||
if len(b) == 0 || err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
|
||||
b = b[:len(b)-1]
|
||||
if br.Ref != nil {
|
||||
ap.WriteItemJSONProp(&b, "ref", br.Ref)
|
||||
}
|
||||
ap.Write(&b, '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (br *Branch) UnmarshalJSON(data []byte) error {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
br.Ref = ap.JSONGetItem(val, "ref")
|
||||
|
||||
return ap.OnObject(&br.Object, func(a *ap.Object) error {
|
||||
return ap.LoadObject(val, a)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
const (
|
||||
CommitType ap.ActivityVocabularyType = "Commit"
|
||||
)
|
||||
|
||||
type Commit struct {
|
||||
ap.Object
|
||||
// Created time at which the commit was written by its author
|
||||
Created time.Time `jsonld:"created,omitempty"`
|
||||
// Committed time at which the commit was committed by its committer
|
||||
Committed time.Time `jsonld:"committed,omitempty"`
|
||||
}
|
||||
|
||||
// CommitNew initializes a Commit type Object
|
||||
func CommitNew() *Commit {
|
||||
a := ap.ObjectNew(CommitType)
|
||||
o := Commit{Object: *a}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (c Commit) MarshalJSON() ([]byte, error) {
|
||||
b, err := c.Object.MarshalJSON()
|
||||
if len(b) == 0 || err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
|
||||
b = b[:len(b)-1]
|
||||
if !c.Created.IsZero() {
|
||||
ap.WriteTimeJSONProp(&b, "created", c.Created)
|
||||
}
|
||||
if !c.Committed.IsZero() {
|
||||
ap.WriteTimeJSONProp(&b, "committed", c.Committed)
|
||||
}
|
||||
ap.Write(&b, '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *Commit) UnmarshalJSON(data []byte) error {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Created = ap.JSONGetTime(val, "created")
|
||||
c.Committed = ap.JSONGetTime(val, "committed")
|
||||
|
||||
return ap.OnObject(&c.Object, func(a *ap.Object) error {
|
||||
return ap.LoadObject(val, a)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
// GetItemByType instantiates a new ForgeFed object if the type matches
|
||||
// otherwise it defaults to existing activitypub package typer function.
|
||||
func GetItemByType(typ ap.ActivityVocabularyType) (ap.Item, error) {
|
||||
if typ == CommitType {
|
||||
return CommitNew(), nil
|
||||
} else if typ == BranchType {
|
||||
return BranchNew(), nil
|
||||
} else if typ == RepositoryType {
|
||||
return RepositoryNew(""), nil
|
||||
} else if typ == PushType {
|
||||
return PushNew(), nil
|
||||
} else if typ == TicketType {
|
||||
return TicketNew(), nil
|
||||
}
|
||||
return ap.GetItemByType(typ)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
const (
|
||||
PushType ap.ActivityVocabularyType = "Push"
|
||||
)
|
||||
|
||||
type Push struct {
|
||||
ap.Object
|
||||
// Target the specific repo history tip onto which the commits were added
|
||||
Target ap.Item `jsonld:"target,omitempty"`
|
||||
// HashBefore hash before adding the new commits
|
||||
HashBefore ap.Item `jsonld:"hashBefore,omitempty"`
|
||||
// HashAfter hash before adding the new commits
|
||||
HashAfter ap.Item `jsonld:"hashAfter,omitempty"`
|
||||
}
|
||||
|
||||
// PushNew initializes a Push type Object
|
||||
func PushNew() *Push {
|
||||
a := ap.ObjectNew(PushType)
|
||||
o := Push{Object: *a}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (p Push) MarshalJSON() ([]byte, error) {
|
||||
b, err := p.Object.MarshalJSON()
|
||||
if len(b) == 0 || err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
|
||||
b = b[:len(b)-1]
|
||||
if p.Target != nil {
|
||||
ap.WriteItemJSONProp(&b, "target", p.Target)
|
||||
}
|
||||
if p.HashBefore != nil {
|
||||
ap.WriteItemJSONProp(&b, "hashBefore", p.HashBefore)
|
||||
}
|
||||
if p.HashAfter != nil {
|
||||
ap.WriteItemJSONProp(&b, "hashAfter", p.HashAfter)
|
||||
}
|
||||
ap.Write(&b, '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *Push) UnmarshalJSON(data []byte) error {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Target = ap.JSONGetItem(val, "target")
|
||||
c.HashBefore = ap.JSONGetItem(val, "hashBefore")
|
||||
c.HashAfter = ap.JSONGetItem(val, "hashAfter")
|
||||
|
||||
return ap.OnObject(&c.Object, func(a *ap.Object) error {
|
||||
return ap.LoadObject(val, a)
|
||||
})
|
||||
}
|
|
@ -21,15 +21,6 @@ type Repository struct {
|
|||
Forks ap.Item `jsonld:"forks,omitempty"`
|
||||
}
|
||||
|
||||
// GetItemByType instantiates a new Repository object if the type matches
|
||||
// otherwise it defaults to existing activitypub package typer function.
|
||||
func GetItemByType(typ ap.ActivityVocabularyType) (ap.Item, error) {
|
||||
if typ == RepositoryType {
|
||||
return RepositoryNew(""), nil
|
||||
}
|
||||
return ap.GetItemByType(typ)
|
||||
}
|
||||
|
||||
// RepositoryNew initializes a Repository type actor
|
||||
func RepositoryNew(id ap.ID) *Repository {
|
||||
a := ap.ActorNew(id, RepositoryType)
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
const (
|
||||
TicketType ap.ActivityVocabularyType = "Ticket"
|
||||
)
|
||||
|
||||
type Ticket struct {
|
||||
ap.Object
|
||||
// Dependants Collection of Tickets which depend on this ticket
|
||||
Dependants ap.ItemCollection `jsonld:"dependants,omitempty"`
|
||||
// Dependencies Collection of Tickets on which this ticket depends
|
||||
Dependencies ap.ItemCollection `jsonld:"dependencies,omitempty"`
|
||||
// IsResolved Whether the work on this ticket is done
|
||||
IsResolved bool `jsonld:"isResolved,omitempty"`
|
||||
// ResolvedBy If the work on this ticket is done, who marked the ticket as resolved, or which activity did so
|
||||
ResolvedBy ap.Item `jsonld:"resolvedBy,omitempty"`
|
||||
// Resolved When the ticket has been marked as resolved
|
||||
Resolved time.Time `jsonld:"resolved,omitempty"`
|
||||
}
|
||||
|
||||
// TicketNew initializes a Ticket type Object
|
||||
func TicketNew() *Ticket {
|
||||
a := ap.ObjectNew(TicketType)
|
||||
o := Ticket{Object: *a}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (t Ticket) MarshalJSON() ([]byte, error) {
|
||||
b, err := t.Object.MarshalJSON()
|
||||
if len(b) == 0 || err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
|
||||
b = b[:len(b)-1]
|
||||
if t.Dependants != nil {
|
||||
ap.WriteItemCollectionJSONProp(&b, "dependants", t.Dependants)
|
||||
}
|
||||
if t.Dependencies != nil {
|
||||
ap.WriteItemCollectionJSONProp(&b, "dependencies", t.Dependencies)
|
||||
}
|
||||
ap.WriteBoolJSONProp(&b, "isResolved", t.IsResolved)
|
||||
if t.ResolvedBy != nil {
|
||||
ap.WriteItemJSONProp(&b, "resolvedBy", t.ResolvedBy)
|
||||
}
|
||||
if !t.Resolved.IsZero() {
|
||||
ap.WriteTimeJSONProp(&b, "resolved", t.Resolved)
|
||||
}
|
||||
ap.Write(&b, '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (t *Ticket) UnmarshalJSON(data []byte) error {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Dependants = ap.JSONGetItems(val, "dependants")
|
||||
t.Dependencies = ap.JSONGetItems(val, "dependencies")
|
||||
t.IsResolved = ap.JSONGetBoolean(val, "isResolved")
|
||||
t.ResolvedBy = ap.JSONGetItem(val, "resolvedBy")
|
||||
t.Resolved = ap.JSONGetTime(val, "resolved")
|
||||
|
||||
return ap.OnObject(&t.Object, func(a *ap.Object) error {
|
||||
return ap.LoadObject(val, a)
|
||||
})
|
||||
}
|
|
@ -137,7 +137,7 @@ func PersonOutbox(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
|
||||
feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
|
@ -145,19 +145,19 @@ func PersonOutbox(ctx *context.APIContext) {
|
|||
IncludePrivate: false,
|
||||
OnlyPerformedBy: true,
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("Couldn't fetch outbox", err)
|
||||
}
|
||||
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link + "/outbox"))
|
||||
for _, action := range feed {
|
||||
/*if action.OpType == ExampleType {
|
||||
activity := ap.ExampleNew()
|
||||
if action.OpType == models.ActionCommentIssue {
|
||||
log.Debug("action", action)
|
||||
activity := ap.Note{Type: ap.NoteType, Content: ap.NaturalLanguageValuesNew()}
|
||||
activity.Content.Set("en", ap.Content(action.Content))
|
||||
outbox.OrderedItems.Append(activity)
|
||||
}*/
|
||||
log.Debug(action.Content)
|
||||
}
|
||||
}
|
||||
outbox.TotalItems = uint(len(outbox.OrderedItems))
|
||||
|
||||
|
@ -181,7 +181,7 @@ func PersonFollowing(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
|
||||
users, err := user_model.GetUserFollowing(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
|
@ -189,7 +189,7 @@ func PersonFollowing(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
following := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
following := ap.OrderedCollectionNew(ap.IRI(link + "/following"))
|
||||
following.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
|
@ -218,7 +218,7 @@ func PersonFollowers(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
|
||||
users, err := user_model.GetUserFollowers(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
|
@ -226,7 +226,7 @@ func PersonFollowers(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link + "/followers"))
|
||||
followers.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
|
@ -254,7 +254,7 @@ func PersonLiked(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||
Actor: ctx.Doer,
|
||||
|
@ -266,7 +266,7 @@ func PersonLiked(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
liked := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
liked := ap.OrderedCollectionNew(ap.IRI(link + "/liked"))
|
||||
liked.TotalItems = uint(count)
|
||||
|
||||
for _, repo := range repos {
|
||||
|
|
|
@ -42,7 +42,7 @@ func Repo(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name
|
||||
repo := forgefed.RepositoryNew(ap.IRI(link))
|
||||
|
||||
repo.Name = ap.NaturalLanguageValuesNew()
|
||||
|
@ -52,7 +52,7 @@ func Repo(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
repo.AttributedTo = ap.IRI(strings.TrimSuffix(link, "/"+ctx.Repo.Repository.Name))
|
||||
repo.AttributedTo = ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name)
|
||||
|
||||
repo.Summary = ap.NaturalLanguageValuesNew()
|
||||
err = repo.Summary.Set("en", ap.Content(ctx.Repo.Repository.Description))
|
||||
|
@ -129,7 +129,7 @@ func RepoOutbox(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name
|
||||
|
||||
feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
|
@ -143,7 +143,7 @@ func RepoOutbox(ctx *context.APIContext) {
|
|||
ctx.ServerError("Couldn't fetch outbox", err)
|
||||
}
|
||||
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link + "/outbox"))
|
||||
for _, action := range feed {
|
||||
/*if action.OpType == ExampleType {
|
||||
activity := ap.ExampleNew()
|
||||
|
@ -178,7 +178,7 @@ func RepoFollowers(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name
|
||||
|
||||
users, err := user_model.GetUserFollowers(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
|
@ -186,7 +186,7 @@ func RepoFollowers(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link + "/followers"))
|
||||
followers.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
|
|
Loading…
Reference in New Issue