Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240package pull
import ( "context" "log/slog" "time"
"github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/syntax" lexutil "github.com/bluesky-social/indigo/lex/util" "tangled.org/core/api/tangled" "tangled.org/core/appview/config" "tangled.org/core/appview/db" pulls_indexer "tangled.org/core/appview/indexer/pulls" "tangled.org/core/appview/mentions" "tangled.org/core/appview/models" "tangled.org/core/appview/notify" "tangled.org/core/appview/session" "tangled.org/core/idresolver" "tangled.org/core/rbac" "tangled.org/core/tid")
type Service struct { config *config.Config db *db.DB enforcer *rbac.Enforcer indexer *pulls_indexer.Indexer logger *slog.Logger notifier notify.Notifier idResolver *idresolver.Resolver mentionsResolver *mentions.Resolver}
func NewService( logger *slog.Logger, config *config.Config, db *db.DB, enforcer *rbac.Enforcer, notifier notify.Notifier, idResolver *idresolver.Resolver, mentionsResolver *mentions.Resolver, indexer *pulls_indexer.Indexer,) Service { return Service{ config, db, enforcer, indexer, logger, notifier, idResolver, mentionsResolver, }}
// SubmitPull creates a new PR or resubmits existing PR.// `pull` can be `nil` for creating a new PR.func (s *Service) SubmitPull( ctx context.Context, pull *models.Pull2, target models.PullTarget, source *models.PullSource2, patch, title, body string,) error { l := s.logger.With("method", "NewPullSubmission") sess := session.FromContext(ctx) if sess == nil { l.Error("user session is missing in context") return ErrForbidden } sessDid := sess.Data.AccountDID l = l.With("did", sessDid)
var ( did syntax.DID rkey syntax.RecordKey ) if pull == nil { // new pr did = sessDid rkey = syntax.RecordKey(tid.TID()) } else { // resubmit if sessDid != pull.Did { l.Error("only author can edit the pull") return ErrForbidden } did = pull.Did rkey = pull.Rkey }
mentions, references := s.mentionsResolver.Resolve(ctx, body)
round := models.PullRound{ Did: did, Rkey: rkey, Target: target, Source: source, Patch: patch, Title: title, Body: body, Mentions: mentions, References: references, Created: time.Now(), } if err := round.Validate(); err != nil { l.Error("validation error", "err", err) return ErrValidationFail }
atpclient := sess.APIClient() record := round.AsRecord()
var exCid *string if pull != nil { x := pull.CID().String() exCid = &x } resp, err := atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, SwapRecord: exCid, Record: &lexutil.LexiconTypeDecoder{ Val: &record, }, }) if err != nil { l.Error("atproto.RepoPutRecord failed", "err", err) return ErrPDSFail } round.Cid = syntax.CID(resp.Cid)
tx, err := s.db.BeginTx(ctx, nil) if err != nil { l.Error("db.BeginTx failed", "err", err) return ErrDatabaseFail } defer tx.Rollback()
if err := db.NewPullRound(tx, 0, &round); err != nil { l.Error("db.UpdatePull2 failed", "err", err) return ErrDatabaseFail }
if err = tx.Commit(); err != nil { l.Error("tx.Commit failed", "err", err) return ErrDatabaseFail }
if pull == nil { // s.notifier.NewPull(ctx, &round) } else { pull.Submissions = append(pull.Submissions, &round) // s.notifier.ResubmitPull(ctx, &round) }
return nil}
func (s *Service) DeletePull(ctx context.Context, pull *models.Pull2) error { l := s.logger.With("method", "DeletePull") sess := session.FromContext(ctx) if sess == nil { l.Error("user session is missing in context") return ErrForbidden } sessDid := sess.Data.AccountDID l = l.With("did", sessDid)
if sessDid != pull.Did { l.Error("only author can delete the pull") return ErrForbidden }
tx, err := s.db.BeginTx(ctx, nil) if err != nil { l.Error("db.BeginTx failed", "err", err) return ErrDatabaseFail } defer tx.Rollback()
if err := db.DeletePull2(tx, pull.AtUri()); err != nil { l.Error("db.DeletePull2 failed", "err", err) return ErrDatabaseFail }
atpclient := sess.APIClient() _, err = atproto.RepoDeleteRecord(ctx, atpclient, &atproto.RepoDeleteRecord_Input{ Collection: tangled.RepoIssueNSID, Repo: pull.Did.String(), Rkey: pull.Rkey.String(), }) if err != nil { l.Error("atproto.RepoDeleteRecord failed", "err", err) return ErrPDSFail }
if err := tx.Commit(); err != nil { l.Error("tx.Commit failed", "err", err) return ErrDatabaseFail }
pull.State = models.PullDeleted
// s.notifier.DeletePull(ctx, pull) return nil}
func (s *Service) ListPulls(ctx context.Context, repo *models.Repo, searchOpts models.PullSearchOptions) ([]*models.Pull2, error) { l := s.logger.With("method", "ListPulls")
var pulls []*models.Pull2 var err error if searchOpts.Keyword != "" { res, err := s.indexer.Search(ctx, searchOpts) if err != nil { l.Error("failed to search for pulls", "err", err) return nil, ErrIndexerFail } l.Debug("searched pulls with indexer", "count", len(res.Hits)) pulls, err = db.GetPulls2(s.db, db.FilterIn("id", res.Hits)) if err != nil { l.Error("failed to get pulls", "err", err) return nil, ErrDatabaseFail } } else { pulls, err = db.GetPullsPaginated( s.db, searchOpts.Page, db.FilterEq("repo_at", repo.RepoAt()), db.FilterEq("state", searchOpts.State), ) if err != nil { l.Error("failed to get pulls", "err", err) return nil, ErrDatabaseFail } } return pulls, nil}