···
9
+
"github.com/bluesky-social/indigo/api/atproto"
10
+
"github.com/bluesky-social/indigo/atproto/syntax"
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
+
issues_indexer "tangled.org/core/appview/indexer/issues"
16
+
"tangled.org/core/appview/models"
17
+
"tangled.org/core/appview/notify"
18
+
"tangled.org/core/appview/pages/markup"
19
+
"tangled.org/core/appview/session"
20
+
"tangled.org/core/appview/validator"
21
+
"tangled.org/core/idresolver"
22
+
"tangled.org/core/tid"
25
+
type Service struct {
26
+
config *config.Config
28
+
indexer *issues_indexer.Indexer
30
+
notifier notify.Notifier
31
+
idResolver *idresolver.Resolver
32
+
validator *validator.Validator
36
+
logger *slog.Logger,
37
+
config *config.Config,
39
+
notifier notify.Notifier,
40
+
idResolver *idresolver.Resolver,
41
+
indexer *issues_indexer.Indexer,
42
+
validator *validator.Validator,
56
+
ErrUnAuthorized = errors.New("unauthorized operation")
57
+
ErrDatabaseFail = errors.New("db op fail")
58
+
ErrPDSFail = errors.New("pds op fail")
59
+
ErrValidationFail = errors.New("issue validation fail")
62
+
func (s *Service) NewIssue(ctx context.Context, repo *models.Repo, title, body string) (*models.Issue, error) {
63
+
l := s.logger.With("method", "NewIssue")
64
+
sess := session.FromContext(ctx)
66
+
l.Error("user session is missing in context")
67
+
return nil, ErrUnAuthorized
69
+
authorDid := sess.Data.AccountDID
70
+
l = l.With("did", authorDid)
72
+
// mentions, references := s.refResolver.Resolve(ctx, body)
73
+
mentions := func() []syntax.DID {
74
+
rawMentions := markup.FindUserMentions(body)
75
+
idents := s.idResolver.ResolveIdents(ctx, rawMentions)
76
+
l.Debug("parsed mentions", "raw", rawMentions, "idents", idents)
77
+
var mentions []syntax.DID
78
+
for _, ident := range idents {
79
+
if ident != nil && !ident.Handle.IsInvalidHandle() {
80
+
mentions = append(mentions, ident.DID)
86
+
issue := models.Issue{
87
+
RepoAt: repo.RepoAt(),
92
+
Did: authorDid.String(),
93
+
Created: time.Now(),
97
+
if err := s.validator.ValidateIssue(&issue); err != nil {
98
+
l.Error("validation error", "err", err)
99
+
return nil, ErrValidationFail
102
+
tx, err := s.db.BeginTx(ctx, nil)
104
+
l.Error("db.BeginTx failed", "err", err)
105
+
return nil, ErrDatabaseFail
107
+
defer tx.Rollback()
109
+
if err := db.PutIssue(tx, &issue); err != nil {
110
+
l.Error("db.PutIssue failed", "err", err)
111
+
return nil, ErrDatabaseFail
114
+
atpclient := sess.APIClient()
115
+
record := issue.AsRecord()
116
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
117
+
Repo: authorDid.String(),
118
+
Collection: tangled.RepoIssueNSID,
120
+
Record: &lexutil.LexiconTypeDecoder{
125
+
l.Error("atproto.RepoPutRecord failed", "err", err)
126
+
return nil, ErrPDSFail
128
+
if err = tx.Commit(); err != nil {
129
+
l.Error("tx.Commit failed", "err", err)
130
+
return nil, ErrDatabaseFail
133
+
s.notifier.NewIssue(ctx, &issue, mentions)
137
+
func (s *Service) GetIssues(ctx context.Context, repo *models.Repo, searchOpts models.IssueSearchOptions) ([]models.Issue, error) {
138
+
l := s.logger.With("method", "EditIssue")
140
+
var issues []models.Issue
142
+
if searchOpts.Keyword != "" {
143
+
res, err := s.indexer.Search(ctx, searchOpts)
145
+
l.Error("failed to search for issues", "err", err)
148
+
l.Debug("searched issues with indexer", "count", len(res.Hits))
149
+
issues, err = db.GetIssues(s.db, db.FilterIn("id", res.Hits))
151
+
l.Error("failed to get issues", "err", err)
156
+
if searchOpts.IsOpen {
159
+
issues, err = db.GetIssuesPaginated(
162
+
db.FilterEq("repo_at", repo.RepoAt()),
163
+
db.FilterEq("open", openInt),
166
+
l.Error("failed to get issues", "err", err)
174
+
func (s *Service) EditIssue(ctx context.Context, issue *models.Issue) error {
175
+
l := s.logger.With("method", "EditIssue")
176
+
sess := session.FromContext(ctx)
178
+
l.Error("user session is missing in context")
179
+
return ErrUnAuthorized
181
+
authorDid := sess.Data.AccountDID
182
+
l = l.With("did", authorDid)
184
+
if err := s.validator.ValidateIssue(issue); err != nil {
185
+
l.Error("validation error", "err", err)
186
+
return ErrValidationFail
189
+
tx, err := s.db.BeginTx(ctx, nil)
191
+
l.Error("db.BeginTx failed", "err", err)
192
+
return ErrDatabaseFail
194
+
defer tx.Rollback()
196
+
if err := db.PutIssue(tx, issue); err != nil {
197
+
l.Error("db.PutIssue failed", "err", err)
198
+
return ErrDatabaseFail
201
+
atpclient := sess.APIClient()
202
+
record := issue.AsRecord()
204
+
ex, err := atproto.RepoGetRecord(ctx, atpclient, "", tangled.RepoIssueNSID, issue.Did, issue.Rkey)
206
+
l.Error("atproto.RepoGetRecord failed", "err", err)
209
+
_, err = atproto.RepoPutRecord(ctx, atpclient, &atproto.RepoPutRecord_Input{
210
+
Collection: tangled.RepoIssueNSID,
211
+
SwapRecord: ex.Cid,
212
+
Record: &lexutil.LexiconTypeDecoder{
217
+
l.Error("atproto.RepoPutRecord failed", "err", err)
221
+
if err = tx.Commit(); err != nil {
222
+
l.Error("tx.Commit failed", "err", err)
223
+
return ErrDatabaseFail
226
+
// TODO: notify PutIssue
231
+
func (s *Service) DeleteIssue(ctx context.Context, issue *models.Issue) error {
232
+
l := s.logger.With("method", "DeleteIssue")
233
+
sess := session.FromContext(ctx)
235
+
l.Error("user session is missing in context")
236
+
return ErrUnAuthorized
238
+
authorDid := sess.Data.AccountDID
239
+
l = l.With("did", authorDid)
241
+
tx, err := s.db.BeginTx(ctx, nil)
243
+
l.Error("db.BeginTx failed", "err", err)
244
+
return ErrDatabaseFail
246
+
defer tx.Rollback()
248
+
if err := db.DeleteIssues(tx, db.FilterEq("id", issue.Id)); err != nil {
249
+
l.Error("db.DeleteIssues failed", "err", err)
250
+
return ErrDatabaseFail
253
+
atpclient := sess.APIClient()
254
+
_, err = atproto.RepoDeleteRecord(ctx, atpclient, &atproto.RepoDeleteRecord_Input{
255
+
Collection: tangled.RepoIssueNSID,
260
+
l.Error("atproto.RepoDeleteRecord failed", "err", err)
264
+
if err := tx.Commit(); err != nil {
265
+
l.Error("tx.Commit failed", "err", err)
266
+
return ErrDatabaseFail
269
+
s.notifier.DeleteIssue(ctx, issue)