···
+
"github.com/bluesky-social/indigo/api/atproto"
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
+
lexutil "github.com/bluesky-social/indigo/lex/util"
+
"tangled.org/core/api/tangled"
+
"tangled.org/core/appview/config"
+
"tangled.org/core/appview/db"
+
"tangled.org/core/appview/models"
+
"tangled.org/core/appview/notify"
+
"tangled.org/core/appview/refresolver"
+
type IssueService struct {
+
notifier notify.Notifier
+
refResolver *refresolver.Resolver
+
notifier notify.Notifier,
+
refResolver *refresolver.Resolver,
+
ErrCtxMissing = errors.New("context values are missing")
+
ErrDatabaseFail = errors.New("db op fail")
+
ErrPDSFail = errors.New("pds op fail")
+
ErrValidationFail = errors.New("issue validation fail")
+
// TODO: NewIssue should return typed errors
+
func (s *IssueService) NewIssue(ctx context.Context, repo *models.Repo, title, body string) (*models.Issue, error) {
+
l := s.logger.With("method", "NewIssue")
+
sess, ok := fromContext(ctx)
+
l.Error("user session is missing in context")
+
return nil, ErrCtxMissing
+
authorDid := sess.Data.AccountDID
+
l = l.With("did", authorDid)
+
mentions, references := s.refResolver.Resolve(ctx, body)
+
Did: authorDid.String(),
+
References: references,
+
// TODO: validate the issue
+
tx, err := s.db.BeginTx(ctx, nil)
+
l.Error("db.BeginTx failed", "err", err)
+
return nil, ErrDatabaseFail
+
if err := db.PutIssue(tx, &issue); err != nil {
+
l.Error("db.PutIssue failed", "err", err)
+
return nil, ErrDatabaseFail
+
atpclient := sess.APIClient()
+
record := issue.AsRecord()
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
+
Repo: authorDid.String(),
+
Collection: tangled.RepoIssueNSID,
+
Record: &lexutil.LexiconTypeDecoder{
+
l.Error("atproto.RepoPutRecord failed", "err", err)
+
if err = tx.Commit(); err != nil {
+
l.Error("tx.Commit failed", "err", err)
+
return nil, ErrDatabaseFail
+
s.notifier.NewIssue(ctx, &issue, mentions)
+
func (s *IssueService) EditIssue(ctx context.Context, issue *models.Issue) error {
+
l := s.logger.With("method", "EditIssue")
+
sess, ok := fromContext(ctx)
+
l.Error("user session is missing in context")
+
authorDid := sess.Data.AccountDID
+
l = l.With("did", authorDid)
+
// TODO: validate issue
+
tx, err := s.db.BeginTx(ctx, nil)
+
l.Error("db.BeginTx failed", "err", err)
+
if err := db.PutIssue(tx, issue); err != nil {
+
l.Error("db.PutIssue failed", "err", err)
+
atpclient := sess.APIClient()
+
record := issue.AsRecord()
+
ex, err := atproto.RepoGetRecord(ctx, atpclient, "", tangled.RepoIssueNSID, issue.Did, issue.Rkey)
+
l.Error("atproto.RepoGetRecord failed", "err", err)
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
+
Collection: tangled.RepoIssueNSID,
+
Record: &lexutil.LexiconTypeDecoder{
+
l.Error("atproto.RepoPutRecord failed", "err", err)
+
if err = tx.Commit(); err != nil {
+
l.Error("tx.Commit failed", "err", err)
+
// TODO: notify PutIssue
+
func (s *IssueService) DeleteIssue(ctx context.Context, issue *models.Issue) error {
+
l := s.logger.With("method", "DeleteIssue")
+
sess, ok := fromContext(ctx)
+
authorDid := sess.Data.AccountDID
+
l = l.With("did", authorDid)
+
tx, err := s.db.BeginTx(ctx, nil)
+
l.Error("db.BeginTx failed", "err", err)
+
if err := db.DeleteIssues(tx, issue.Did, issue.Rkey); err != nil {
+
l.Error("db.DeleteIssues failed", "err", err)
+
atpclient := sess.APIClient()
+
_, err = atproto.RepoDeleteRecord(ctx, atpclient, &atproto.RepoDeleteRecord_Input{
+
Collection: tangled.RepoIssueNSID,
+
l.Error("atproto.RepoDeleteRecord failed", "err", err)
+
if err := tx.Commit(); err != nil {
+
l.Error("tx.Commit failed", "err", err)
+
s.notifier.DeleteIssue(ctx, issue)
+
func fromContext(ctx context.Context) (*oauth.ClientSession, bool) {
+
sess, ok := ctx.Value("sess").(*oauth.ClientSession)