···
+
"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"
+
issues_indexer "tangled.org/core/appview/indexer/issues"
+
"tangled.org/core/appview/models"
+
"tangled.org/core/appview/notify"
+
"tangled.org/core/appview/pages/markup"
+
"tangled.org/core/appview/session"
+
"tangled.org/core/appview/validator"
+
"tangled.org/core/idresolver"
+
"tangled.org/core/rbac"
+
enforcer *rbac.Enforcer
+
indexer *issues_indexer.Indexer
+
notifier notify.Notifier
+
idResolver *idresolver.Resolver
+
validator *validator.Validator
+
enforcer *rbac.Enforcer,
+
notifier notify.Notifier,
+
idResolver *idresolver.Resolver,
+
indexer *issues_indexer.Indexer,
+
validator *validator.Validator,
+
ErrUnAuthenticated = errors.New("user session missing")
+
ErrForbidden = errors.New("unauthorized operation")
+
ErrDatabaseFail = errors.New("db op fail")
+
ErrPDSFail = errors.New("pds op fail")
+
ErrValidationFail = errors.New("issue validation fail")
+
func (s *Service) NewIssue(ctx context.Context, repo *models.Repo, title, body string) (*models.Issue, error) {
+
l := s.logger.With("method", "NewIssue")
+
sess := session.FromContext(ctx)
+
l.Error("user session is missing in context")
+
return nil, ErrForbidden
+
authorDid := sess.Data.AccountDID
+
l = l.With("did", authorDid)
+
// mentions, references := s.refResolver.Resolve(ctx, body)
+
mentions := func() []syntax.DID {
+
rawMentions := markup.FindUserMentions(body)
+
idents := s.idResolver.ResolveIdents(ctx, rawMentions)
+
l.Debug("parsed mentions", "raw", rawMentions, "idents", idents)
+
var mentions []syntax.DID
+
for _, ident := range idents {
+
if ident != nil && !ident.Handle.IsInvalidHandle() {
+
mentions = append(mentions, ident.DID)
+
Did: authorDid.String(),
+
if err := s.validator.ValidateIssue(&issue); err != nil {
+
l.Error("validation error", "err", err)
+
return nil, ErrValidationFail
+
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 *Service) GetIssues(ctx context.Context, repo *models.Repo, searchOpts models.IssueSearchOptions) ([]models.Issue, error) {
+
l := s.logger.With("method", "EditIssue")
+
var issues []models.Issue
+
if searchOpts.Keyword != "" {
+
res, err := s.indexer.Search(ctx, searchOpts)
+
l.Error("failed to search for issues", "err", err)
+
l.Debug("searched issues with indexer", "count", len(res.Hits))
+
issues, err = db.GetIssues(s.db, db.FilterIn("id", res.Hits))
+
l.Error("failed to get issues", "err", err)
+
issues, err = db.GetIssuesPaginated(
+
db.FilterEq("repo_at", repo.RepoAt()),
+
db.FilterEq("open", openInt),
+
l.Error("failed to get issues", "err", err)
+
func (s *Service) EditIssue(ctx context.Context, issue *models.Issue) error {
+
l := s.logger.With("method", "EditIssue")
+
sess := session.FromContext(ctx)
+
l.Error("user session is missing in context")
+
sessDid := sess.Data.AccountDID
+
l = l.With("did", sessDid)
+
if sessDid != syntax.DID(issue.Did) {
+
l.Error("only author can edit the issue")
+
if err := s.validator.ValidateIssue(issue); err != nil {
+
l.Error("validation error", "err", err)
+
return ErrValidationFail
+
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 *Service) DeleteIssue(ctx context.Context, issue *models.Issue) error {
+
l := s.logger.With("method", "DeleteIssue")
+
sess := session.FromContext(ctx)
+
l.Error("user session is missing in context")
+
sessDid := sess.Data.AccountDID
+
l = l.With("did", sessDid)
+
if sessDid != syntax.DID(issue.Did) {
+
l.Error("only author can edit the issue")
+
tx, err := s.db.BeginTx(ctx, nil)
+
l.Error("db.BeginTx failed", "err", err)
+
if err := db.DeleteIssues(tx, db.FilterEq("id", issue.Id)); 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)