From e5f8d8e118e656f6b2f961c7cb43bbbc590d0e3b Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Wed, 20 Aug 2025 19:37:59 +0300 Subject: [PATCH] appview: ingest issue records Change-Id: qtptkmynqwtpswytkxnvwrlxmyklxyny Signed-off-by: Anirudh Oppiliappan --- appview/ingester.go | 75 ++++++++++++++++++++++++++++++++++++++---- appview/state/state.go | 1 + 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/appview/ingester.go b/appview/ingester.go index 6eef025f..a5ff533f 100644 --- a/appview/ingester.go +++ b/appview/ingester.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log/slog" + "strings" "time" "github.com/bluesky-social/indigo/atproto/syntax" @@ -14,6 +15,7 @@ import ( "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview/config" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/serververify" "tangled.sh/tangled.sh/core/idresolver" "tangled.sh/tangled.sh/core/rbac" @@ -61,15 +63,17 @@ func (i *Ingester) Ingest() processFunc { case tangled.ActorProfileNSID: err = i.ingestProfile(e) case tangled.SpindleMemberNSID: - err = i.ingestSpindleMember(e) + err = i.ingestSpindleMember(ctx, e) case tangled.SpindleNSID: - err = i.ingestSpindle(e) + err = i.ingestSpindle(ctx, e) case tangled.KnotMemberNSID: err = i.ingestKnotMember(e) case tangled.KnotNSID: err = i.ingestKnot(e) case tangled.StringNSID: err = i.ingestString(e) + case tangled.RepoIssueNSID: + err = i.ingestIssue(ctx, e) } l = i.Logger.With("nsid", e.Commit.Collection) } @@ -340,7 +344,7 @@ func (i *Ingester) ingestProfile(e *models.Event) error { return nil } -func (i *Ingester) ingestSpindleMember(e *models.Event) error { +func (i *Ingester) ingestSpindleMember(ctx context.Context, e *models.Event) error { did := e.Did var err error @@ -363,7 +367,7 @@ func (i *Ingester) ingestSpindleMember(e *models.Event) error { return fmt.Errorf("failed to enforce permissions: %w", err) } - memberId, err := i.IdResolver.ResolveIdent(context.Background(), record.Subject) + memberId, err := i.IdResolver.ResolveIdent(ctx, record.Subject) if err != nil { return err } @@ -446,7 +450,7 @@ func (i *Ingester) ingestSpindleMember(e *models.Event) error { return nil } -func (i *Ingester) ingestSpindle(e *models.Event) error { +func (i *Ingester) ingestSpindle(ctx context.Context, e *models.Event) error { did := e.Did var err error @@ -479,7 +483,7 @@ func (i *Ingester) ingestSpindle(e *models.Event) error { return err } - err = serververify.RunVerification(context.Background(), instance, did, i.Config.Core.Dev) + err = serververify.RunVerification(ctx, instance, did, i.Config.Core.Dev) if err != nil { l.Error("failed to add spindle to db", "err", err, "instance", instance) return err @@ -769,3 +773,62 @@ func (i *Ingester) ingestKnot(e *models.Event) error { return nil } +func (i *Ingester) ingestIssue(ctx context.Context, e *models.Event) error { + did := e.Did + rkey := e.Commit.RKey + + var err error + + l := i.Logger.With("handler", "ingestIssue", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) + l.Info("ingesting record") + + ddb, ok := i.Db.Execer.(*db.DB) + if !ok { + return fmt.Errorf("failed to index issue record, invalid db cast") + } + + switch e.Commit.Operation { + case models.CommitOperationCreate: + raw := json.RawMessage(e.Commit.Record) + record := tangled.RepoIssue{} + err = json.Unmarshal(raw, &record) + if err != nil { + l.Error("invalid record", "err", err) + return err + } + + issue := db.IssueFromRecord(did, rkey, record) + + sanitizer := markup.NewSanitizer() + if st := strings.TrimSpace(sanitizer.SanitizeDescription(issue.Title)); st == "" { + return fmt.Errorf("title is empty after HTML sanitization") + } + if sb := strings.TrimSpace(sanitizer.SanitizeDefault(issue.Body)); sb == "" { + return fmt.Errorf("body is empty after HTML sanitization") + } + + tx, err := ddb.BeginTx(ctx, nil) + if err != nil { + l.Error("failed to begin transaction", "err", err) + return err + } + + err = db.NewIssue(tx, &issue) + if err != nil { + l.Error("failed to create issue", "err", err) + return err + } + + return nil + + case models.CommitOperationUpdate: + // TODO: implement updates + return nil + + case models.CommitOperationDelete: + // TODO: implement issue deletion + return nil + } + + return fmt.Errorf("unknown operation: %s", e.Commit.Operation) +} diff --git a/appview/state/state.go b/appview/state/state.go index 2803e9f8..e0136c66 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -99,6 +99,7 @@ func Make(ctx context.Context, config *config.Config) (*State, error) { tangled.SpindleMemberNSID, tangled.SpindleNSID, tangled.StringNSID, + tangled.RepoIssueNSID, }, nil, slog.Default(), -- 2.43.0