From 19af0c92676ff88953d5be76b8328e6ab2dc7ec5 Mon Sep 17 00:00:00 2001 From: Anthony Wang Date: Fri, 25 Nov 2022 18:45:06 +0000 Subject: [PATCH] Implement loading remote tickets --- modules/activitypub/issue.go | 34 +++++++++++++- modules/activitypub/repo.go | 3 +- routers/api/v1/activitypub/ticket.go | 12 ++++- routers/web/authorize_interaction.go | 70 +++++++++++++++++++++++++--- 4 files changed, 108 insertions(+), 11 deletions(-) diff --git a/modules/activitypub/issue.go b/modules/activitypub/issue.go index fb2e3c4b3..720e6ba91 100644 --- a/modules/activitypub/issue.go +++ b/modules/activitypub/issue.go @@ -6,12 +6,42 @@ package activitypub import ( "context" + "fmt" + "strconv" + issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/forgefed" + issue_service "code.gitea.io/gitea/services/issue" + + ap "github.com/go-ap/activitypub" ) // Create an issue func ReceiveIssue(ctx context.Context, ticket *forgefed.Ticket) error { - // TODO - return nil + // Construct issue + user, err := PersonIRIToUser(ctx, ap.IRI(ticket.AttributedTo.GetLink().String())) + if err != nil { + return err + } + repo, err := RepositoryIRIToRepository(ctx, ap.IRI(ticket.Context.GetLink().String())) + if err != nil { + return err + } + fmt.Println(ticket) + fmt.Println(ticket.Name.String()) + idx, err := strconv.ParseInt(ticket.Name.String()[1:], 10, 64) + if err != nil { + return err + } + issue := &issues_model.Issue{ + ID: idx, + RepoID: repo.ID, + Repo: repo, + Title: ticket.Summary.String(), + PosterID: user.ID, + Poster: user, + Content: ticket.Content.String(), + } + fmt.Println(issue) + return issue_service.NewIssue(repo, issue, nil, nil, nil) } diff --git a/modules/activitypub/repo.go b/modules/activitypub/repo.go index 98b5ab4f7..52c096a26 100644 --- a/modules/activitypub/repo.go +++ b/modules/activitypub/repo.go @@ -15,8 +15,7 @@ import ( // Create a new federated repo from a Repository object func FederatedRepoNew(ctx context.Context, repository *forgefed.Repository) error { - ownerIRI := repository.AttributedTo.GetLink() - user, err := PersonIRIToUser(ctx, ownerIRI) + user, err := PersonIRIToUser(ctx, repository.AttributedTo.GetLink()) if err != nil { return err } diff --git a/routers/api/v1/activitypub/ticket.go b/routers/api/v1/activitypub/ticket.go index 81cc2a53c..9a064673d 100644 --- a/routers/api/v1/activitypub/ticket.go +++ b/routers/api/v1/activitypub/ticket.go @@ -46,6 +46,7 @@ func Ticket(ctx *context.APIContext) { link := setting.AppURL + "api/v1/activitypub/ticket/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name + "/" + ctx.Params("id") ticket := forgefed.TicketNew() + ticket.Type = forgefed.TicketType ticket.ID = ap.IRI(link) repo, err := repo_model.GetRepositoryByOwnerAndNameCtx(ctx, ctx.ContextUser.Name, ctx.Repo.Repository.Name) @@ -64,7 +65,16 @@ func Ticket(ctx *context.APIContext) { return } - ticket.Context = ap.IRI(setting.AppURL + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name) + ticket.Name = ap.NaturalLanguageValuesNew() + // Setting a NaturalLanguageValue to a number causes go-ap's JSON parsing to do weird things + // Workaround: set it to #1 instead of 1 + err = ticket.Name.Set("en", ap.Content("#" + ctx.Params("id"))) + if err != nil { + ctx.ServerError("Set Name", err) + return + } + + ticket.Context = ap.IRI(setting.AppURL + "api/v1/activitypub/repo/" + ctx.ContextUser.Name + "/" + ctx.Repo.Repository.Name) err = issue.LoadPoster() if err != nil { diff --git a/routers/web/authorize_interaction.go b/routers/web/authorize_interaction.go index 3679adef8..e74ada504 100644 --- a/routers/web/authorize_interaction.go +++ b/routers/web/authorize_interaction.go @@ -7,6 +7,7 @@ package web import ( "net/http" "net/url" + "strconv" "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/context" @@ -19,7 +20,7 @@ import ( func AuthorizeInteraction(ctx *context.Context) { uri, err := url.Parse(ctx.Req.URL.Query().Get("uri")) if err != nil { - ctx.ServerError("Could not parse URI", err) + ctx.ServerError("Parse URI", err) return } resp, err := activitypub.Fetch(uri) @@ -27,7 +28,7 @@ func AuthorizeInteraction(ctx *context.Context) { ctx.ServerError("Fetch", err) return } - + ap.ItemTyperFunc = forgefed.GetItemByType ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn object, err := ap.UnmarshalJSON(resp) @@ -35,9 +36,10 @@ func AuthorizeInteraction(ctx *context.Context) { ctx.ServerError("UnmarshalJSON", err) return } - + switch object.GetType() { case ap.PersonType: + // Federated user if err != nil { ctx.ServerError("UnmarshalJSON", err) return @@ -49,12 +51,34 @@ func AuthorizeInteraction(ctx *context.Context) { } name, err := activitypub.PersonIRIToName(object.GetLink()) if err != nil { - ctx.ServerError("personIRIToName", err) + ctx.ServerError("PersonIRIToName", err) return } ctx.Redirect(name) case forgefed.RepositoryType: + // Federated repository err = forgefed.OnRepository(object, func(r *forgefed.Repository) error { + ownerURL, err := url.Parse(r.AttributedTo.GetLink().String()) + if err != nil { + return err + } + // Fetch person object + resp, err := activitypub.Fetch(ownerURL) + if err != nil { + return err + } + // Parse person object + ap.ItemTyperFunc = forgefed.GetItemByType + ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn + object, err := ap.UnmarshalJSON(resp) + if err != nil { + return err + } + // Create federated user + err = user_service.FederatedUserNew(ctx, object.(*ap.Person)) + if err != nil { + return err + } return activitypub.FederatedRepoNew(ctx, r) }) if err != nil { @@ -63,19 +87,53 @@ func AuthorizeInteraction(ctx *context.Context) { } username, reponame, err := activitypub.RepositoryIRIToName(object.GetLink()) if err != nil { - ctx.ServerError("repositoryIRIToName", err) + ctx.ServerError("RepositoryIRIToName", err) return } ctx.Redirect(username + "/" + reponame) case forgefed.TicketType: + // Federated ticket err = forgefed.OnTicket(object, func(t *forgefed.Ticket) error { + // TODO: make sure federated user exists + // Also, refactor this code to reduce the chance of accidentally creating import cycles + repoURL, err := url.Parse(t.Context.GetLink().String()) + if err != nil { + return err + } + // Fetch repository object + resp, err := activitypub.Fetch(repoURL) + if err != nil { + return err + } + // Parse repository object + ap.ItemTyperFunc = forgefed.GetItemByType + ap.JSONItemUnmarshal = forgefed.JSONUnmarshalerFn + object, err := ap.UnmarshalJSON(resp) + if err != nil { + return err + } + // Create federated repo + err = forgefed.OnRepository(object, func(r *forgefed.Repository) error { + return activitypub.FederatedRepoNew(ctx, r) + }) + if err != nil { + return err + } return activitypub.ReceiveIssue(ctx, t) }) if err != nil { ctx.ServerError("ReceiveIssue", err) return } - // TODO: Implement ticketIRIToName and redirect to ticket + username, reponame, idx, err := activitypub.TicketIRIToName(object.GetLink()) + if err != nil { + ctx.ServerError("TicketIRIToName", err) + return + } + ctx.Redirect(username + "/" + reponame + "/issues/" + strconv.FormatInt(idx, 10)) + default: + ctx.ServerError("Not implemented", err) + return } ctx.Status(http.StatusOK)