···
9
+
"github.com/bluesky-social/indigo/api/atproto"
10
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
11
+
lexutil "github.com/bluesky-social/indigo/lex/util"
12
+
"tangled.org/core/api/tangled"
13
+
"tangled.org/core/appview/config"
14
+
"tangled.org/core/appview/db"
15
+
"tangled.org/core/appview/models"
16
+
"tangled.org/core/appview/notify"
17
+
"tangled.org/core/appview/refresolver"
18
+
"tangled.org/core/tid"
21
+
type IssueService struct {
23
+
config *config.Config
25
+
notifier notify.Notifier
26
+
refResolver *refresolver.Resolver
30
+
logger *slog.Logger,
31
+
config *config.Config,
33
+
notifier notify.Notifier,
34
+
refResolver *refresolver.Resolver,
36
+
return IssueService{
46
+
ErrCtxMissing = errors.New("context values are missing")
47
+
ErrDatabaseFail = errors.New("db op fail")
48
+
ErrPDSFail = errors.New("pds op fail")
49
+
ErrValidationFail = errors.New("issue validation fail")
52
+
// TODO: NewIssue should return typed errors
53
+
func (s *IssueService) NewIssue(ctx context.Context, repo *models.Repo, title, body string) (*models.Issue, error) {
54
+
l := s.logger.With("method", "NewIssue")
55
+
sess, ok := fromContext(ctx)
57
+
l.Error("user session is missing in context")
58
+
return nil, ErrCtxMissing
60
+
authorDid := sess.Data.AccountDID
61
+
l = l.With("did", authorDid)
63
+
mentions, references := s.refResolver.Resolve(ctx, body)
65
+
issue := models.Issue{
66
+
RepoAt: repo.RepoAt(),
71
+
Did: authorDid.String(),
72
+
Created: time.Now(),
74
+
References: references,
77
+
// TODO: validate the issue
79
+
tx, err := s.db.BeginTx(ctx, nil)
81
+
l.Error("db.BeginTx failed", "err", err)
82
+
return nil, ErrDatabaseFail
86
+
if err := db.PutIssue(tx, &issue); err != nil {
87
+
l.Error("db.PutIssue failed", "err", err)
88
+
return nil, ErrDatabaseFail
91
+
atpclient := sess.APIClient()
92
+
record := issue.AsRecord()
93
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
94
+
Repo: authorDid.String(),
95
+
Collection: tangled.RepoIssueNSID,
97
+
Record: &lexutil.LexiconTypeDecoder{
102
+
l.Error("atproto.RepoPutRecord failed", "err", err)
103
+
return nil, ErrPDSFail
105
+
if err = tx.Commit(); err != nil {
106
+
l.Error("tx.Commit failed", "err", err)
107
+
return nil, ErrDatabaseFail
110
+
s.notifier.NewIssue(ctx, &issue, mentions)
114
+
func (s *IssueService) EditIssue(ctx context.Context, issue *models.Issue) error {
115
+
l := s.logger.With("method", "EditIssue")
116
+
sess, ok := fromContext(ctx)
118
+
l.Error("user session is missing in context")
119
+
return ErrCtxMissing
121
+
authorDid := sess.Data.AccountDID
122
+
l = l.With("did", authorDid)
124
+
// TODO: validate issue
126
+
tx, err := s.db.BeginTx(ctx, nil)
128
+
l.Error("db.BeginTx failed", "err", err)
129
+
return ErrDatabaseFail
131
+
defer tx.Rollback()
133
+
if err := db.PutIssue(tx, issue); err != nil {
134
+
l.Error("db.PutIssue failed", "err", err)
135
+
return ErrDatabaseFail
138
+
atpclient := sess.APIClient()
139
+
record := issue.AsRecord()
141
+
ex, err := atproto.RepoGetRecord(ctx, atpclient, "", tangled.RepoIssueNSID, issue.Did, issue.Rkey)
143
+
l.Error("atproto.RepoGetRecord failed", "err", err)
146
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
147
+
Collection: tangled.RepoIssueNSID,
148
+
SwapRecord: ex.Cid,
149
+
Record: &lexutil.LexiconTypeDecoder{
154
+
l.Error("atproto.RepoPutRecord failed", "err", err)
158
+
if err = tx.Commit(); err != nil {
159
+
l.Error("tx.Commit failed", "err", err)
160
+
return ErrDatabaseFail
163
+
// TODO: notify PutIssue
168
+
func (s *IssueService) DeleteIssue(ctx context.Context, issue *models.Issue) error {
169
+
l := s.logger.With("method", "DeleteIssue")
170
+
sess, ok := fromContext(ctx)
172
+
return ErrCtxMissing
174
+
authorDid := sess.Data.AccountDID
175
+
l = l.With("did", authorDid)
177
+
tx, err := s.db.BeginTx(ctx, nil)
179
+
l.Error("db.BeginTx failed", "err", err)
180
+
return ErrDatabaseFail
182
+
defer tx.Rollback()
184
+
if err := db.DeleteIssues(tx, issue.Did, issue.Rkey); err != nil {
185
+
l.Error("db.DeleteIssues failed", "err", err)
186
+
return ErrDatabaseFail
189
+
atpclient := sess.APIClient()
190
+
_, err = atproto.RepoDeleteRecord(ctx, atpclient, &atproto.RepoDeleteRecord_Input{
191
+
Collection: tangled.RepoIssueNSID,
196
+
l.Error("atproto.RepoDeleteRecord failed", "err", err)
200
+
if err := tx.Commit(); err != nil {
201
+
l.Error("tx.Commit failed", "err", err)
202
+
return ErrDatabaseFail
205
+
s.notifier.DeleteIssue(ctx, issue)
209
+
// TODO: remove this
210
+
func fromContext(ctx context.Context) (*oauth.ClientSession, bool) {
211
+
sess, ok := ctx.Value("sess").(*oauth.ClientSession)