forked from tangled.org/core
Monorepo for Tangled

Revert "telemetry: init telemetry package"

This reverts commit 44f2b1f562faf1f36be90385a699a60991db6b86.

-1
appview/config.go
··· 17 17 CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"` 18 18 AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"` 19 19 AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"` 20 - EnableTelemetry bool `env:"TANGLED_TELEMETRY_ENABLED, default=false"` 21 20 } 22 21 23 22 func LoadConfig(ctx context.Context) (*Config, error) {
+6 -31
appview/db/issues.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "database/sql" 6 5 "time" 7 6 8 7 "github.com/bluesky-social/indigo/atproto/syntax" 9 - "go.opentelemetry.io/otel" 10 - "go.opentelemetry.io/otel/attribute" 11 8 "tangled.sh/tangled.sh/core/appview/pagination" 12 9 ) 13 10 ··· 106 103 return ownerDid, err 107 104 } 108 105 109 - func GetIssues(ctx context.Context, e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) { 110 - ctx, span := otel.Tracer("db").Start(ctx, "GetIssues") 111 - defer span.End() 112 - 113 - span.SetAttributes( 114 - attribute.String("repo_at", repoAt.String()), 115 - attribute.Bool("is_open", isOpen), 116 - attribute.Int("page.offset", page.Offset), 117 - attribute.Int("page.limit", page.Limit), 118 - ) 119 - 106 + func GetIssues(e Execer, repoAt syntax.ATURI, isOpen bool, page pagination.Page) ([]Issue, error) { 120 107 var issues []Issue 121 108 openValue := 0 122 109 if isOpen { 123 110 openValue = 1 124 111 } 125 112 126 - rows, err := e.QueryContext( 127 - ctx, 113 + rows, err := e.Query( 128 114 ` 129 115 with numbered_issue as ( 130 116 select ··· 153 139 body, 154 140 open, 155 141 comment_count 156 - from 142 + from 157 143 numbered_issue 158 - where 144 + where 159 145 row_num between ? and ?`, 160 146 repoAt, openValue, page.Offset+1, page.Offset+page.Limit) 161 147 if err != nil { 162 - span.RecordError(err) 163 148 return nil, err 164 149 } 165 150 defer rows.Close() ··· 170 155 var metadata IssueMetadata 171 156 err := rows.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open, &metadata.CommentCount) 172 157 if err != nil { 173 - span.RecordError(err) 174 158 return nil, err 175 159 } 176 160 177 161 createdTime, err := time.Parse(time.RFC3339, createdAt) 178 162 if err != nil { 179 - span.RecordError(err) 180 163 return nil, err 181 164 } 182 165 issue.Created = createdTime ··· 186 169 } 187 170 188 171 if err := rows.Err(); err != nil { 189 - span.RecordError(err) 190 172 return nil, err 191 173 } 192 174 193 - span.SetAttributes(attribute.Int("issues.count", len(issues))) 194 175 return issues, nil 195 176 } 196 177 ··· 275 256 return issues, nil 276 257 } 277 258 278 - func GetIssue(ctx context.Context, e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { 279 - ctx, span := otel.Tracer("db").Start(ctx, "GetIssue") 280 - defer span.End() 281 - 259 + func GetIssue(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, error) { 282 260 query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?` 283 261 row := e.QueryRow(query, repoAt, issueId) 284 262 ··· 298 276 return &issue, nil 299 277 } 300 278 301 - func GetIssueWithComments(ctx context.Context, e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { 302 - ctx, span := otel.Tracer("db").Start(ctx, "GetIssueWithComments") 303 - defer span.End() 304 - 279 + func GetIssueWithComments(e Execer, repoAt syntax.ATURI, issueId int) (*Issue, []Comment, error) { 305 280 query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?` 306 281 row := e.QueryRow(query, repoAt, issueId) 307 282
+3 -29
appview/db/profile.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "fmt" 6 5 "time" 7 - 8 - "go.opentelemetry.io/otel/attribute" 9 - "go.opentelemetry.io/otel/codes" 10 - "go.opentelemetry.io/otel/trace" 11 6 ) 12 7 13 8 type RepoEvent struct { ··· 88 83 89 84 const TimeframeMonths = 7 90 85 91 - func MakeProfileTimeline(ctx context.Context, e Execer, forDid string) (*ProfileTimeline, error) { 92 - span := trace.SpanFromContext(ctx) 93 - defer span.End() 94 - 95 - span.SetAttributes( 96 - attribute.String("forDid", forDid), 97 - ) 98 - 86 + func MakeProfileTimeline(e Execer, forDid string) (*ProfileTimeline, error) { 99 87 timeline := ProfileTimeline{ 100 88 ByMonth: make([]ByMonth, TimeframeMonths), 101 89 } ··· 104 92 105 93 pulls, err := GetPullsByOwnerDid(e, forDid, timeframe) 106 94 if err != nil { 107 - span.RecordError(err) 108 - span.SetStatus(codes.Error, "error getting pulls by owner did") 109 95 return nil, fmt.Errorf("error getting pulls by owner did: %w", err) 110 96 } 111 97 112 - span.SetAttributes(attribute.Int("pulls.count", len(pulls))) 113 - 114 98 // group pulls by month 115 99 for _, pull := range pulls { 116 100 pullMonth := pull.Created.Month() ··· 128 112 129 113 issues, err := GetIssuesByOwnerDid(e, forDid, timeframe) 130 114 if err != nil { 131 - span.RecordError(err) 132 - span.SetStatus(codes.Error, "error getting issues by owner did") 133 115 return nil, fmt.Errorf("error getting issues by owner did: %w", err) 134 116 } 135 117 136 - span.SetAttributes(attribute.Int("issues.count", len(issues))) 137 - 138 118 for _, issue := range issues { 139 119 issueMonth := issue.Created.Month() 140 120 ··· 149 129 *items = append(*items, &issue) 150 130 } 151 131 152 - repos, err := GetAllReposByDid(ctx, e, forDid) 132 + repos, err := GetAllReposByDid(e, forDid) 153 133 if err != nil { 154 - span.RecordError(err) 155 - span.SetStatus(codes.Error, "error getting all repos by did") 156 134 return nil, fmt.Errorf("error getting all repos by did: %w", err) 157 135 } 158 - 159 - span.SetAttributes(attribute.Int("repos.count", len(repos))) 160 136 161 137 for _, repo := range repos { 162 138 // TODO: get this in the original query; requires COALESCE because nullable 163 139 var sourceRepo *Repo 164 140 if repo.Source != "" { 165 - sourceRepo, err = GetRepoByAtUri(ctx, e, repo.Source) 141 + sourceRepo, err = GetRepoByAtUri(e, repo.Source) 166 142 if err != nil { 167 - span.RecordError(err) 168 - span.SetStatus(codes.Error, "error getting repo by at uri") 169 143 return nil, err 170 144 } 171 145 }
+25 -111
appview/db/pulls.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "database/sql" 6 5 "fmt" 7 6 "log" ··· 11 10 12 11 "github.com/bluekeyes/go-gitdiff/gitdiff" 13 12 "github.com/bluesky-social/indigo/atproto/syntax" 14 - "go.opentelemetry.io/otel/attribute" 15 - "go.opentelemetry.io/otel/trace" 16 13 "tangled.sh/tangled.sh/core/api/tangled" 17 14 "tangled.sh/tangled.sh/core/patchutil" 18 15 "tangled.sh/tangled.sh/core/types" ··· 237 234 return patches 238 235 } 239 236 240 - func NewPull(ctx context.Context, tx *sql.Tx, pull *Pull) error { 241 - span := trace.SpanFromContext(ctx) 242 - defer span.End() 243 - 244 - span.SetAttributes( 245 - attribute.String("repo.at", pull.RepoAt.String()), 246 - attribute.String("owner.did", pull.OwnerDid), 247 - attribute.String("title", pull.Title), 248 - attribute.String("target_branch", pull.TargetBranch), 249 - ) 250 - span.AddEvent("creating new pull request") 251 - 237 + func NewPull(tx *sql.Tx, pull *Pull) error { 252 238 defer tx.Rollback() 253 239 254 240 _, err := tx.Exec(` ··· 256 242 values (?, 1) 257 243 `, pull.RepoAt) 258 244 if err != nil { 259 - span.RecordError(err) 260 245 return err 261 246 } 262 247 ··· 268 253 returning next_pull_id - 1 269 254 `, pull.RepoAt).Scan(&nextId) 270 255 if err != nil { 271 - span.RecordError(err) 272 256 return err 273 257 } 274 258 275 259 pull.PullId = nextId 276 260 pull.State = PullOpen 277 261 278 - span.SetAttributes(attribute.Int("pull.id", pull.PullId)) 279 - span.AddEvent("assigned pull ID") 280 - 281 262 var sourceBranch, sourceRepoAt *string 282 263 if pull.PullSource != nil { 283 264 sourceBranch = &pull.PullSource.Branch ··· 303 284 sourceRepoAt, 304 285 ) 305 286 if err != nil { 306 - span.RecordError(err) 307 287 return err 308 288 } 309 - 310 - span.AddEvent("inserted pull record") 311 289 312 290 _, err = tx.Exec(` 313 291 insert into pull_submissions (pull_id, repo_at, round_number, patch, source_rev) 314 292 values (?, ?, ?, ?, ?) 315 293 `, pull.PullId, pull.RepoAt, 0, pull.Submissions[0].Patch, pull.Submissions[0].SourceRev) 316 294 if err != nil { 317 - span.RecordError(err) 318 295 return err 319 296 } 320 - 321 - span.AddEvent("inserted initial pull submission") 322 297 323 298 if err := tx.Commit(); err != nil { 324 - span.RecordError(err) 325 299 return err 326 300 } 327 301 328 - span.AddEvent("transaction committed successfully") 329 302 return nil 330 303 } 331 304 332 - func GetPullAt(ctx context.Context, e Execer, repoAt syntax.ATURI, pullId int) (syntax.ATURI, error) { 333 - pull, err := GetPull(ctx, e, repoAt, pullId) 305 + func GetPullAt(e Execer, repoAt syntax.ATURI, pullId int) (syntax.ATURI, error) { 306 + pull, err := GetPull(e, repoAt, pullId) 334 307 if err != nil { 335 308 return "", err 336 309 } ··· 343 316 return pullId - 1, err 344 317 } 345 318 346 - func GetPulls(ctx context.Context, e Execer, repoAt syntax.ATURI, state PullState) ([]*Pull, error) { 347 - span := trace.SpanFromContext(ctx) 348 - defer span.End() 349 - 350 - span.SetAttributes( 351 - attribute.String("repoAt", repoAt.String()), 352 - attribute.String("state", state.String()), 353 - ) 354 - span.AddEvent("querying pulls") 355 - 319 + func GetPulls(e Execer, repoAt syntax.ATURI, state PullState) ([]*Pull, error) { 356 320 pulls := make(map[int]*Pull) 357 321 358 - rows, err := e.QueryContext(ctx, ` 322 + rows, err := e.Query(` 359 323 select 360 324 owner_did, 361 325 pull_id, ··· 372 336 where 373 337 repo_at = ? and state = ?`, repoAt, state) 374 338 if err != nil { 375 - span.RecordError(err) 376 339 return nil, err 377 340 } 378 341 defer rows.Close() ··· 394 357 &sourceRepoAt, 395 358 ) 396 359 if err != nil { 397 - span.RecordError(err) 398 360 return nil, err 399 361 } 400 362 401 363 createdTime, err := time.Parse(time.RFC3339, createdAt) 402 364 if err != nil { 403 - span.RecordError(err) 404 365 return nil, err 405 366 } 406 367 pull.Created = createdTime ··· 412 373 if sourceRepoAt.Valid { 413 374 sourceRepoAtParsed, err := syntax.ParseATURI(sourceRepoAt.String) 414 375 if err != nil { 415 - span.RecordError(err) 416 376 return nil, err 417 377 } 418 378 pull.PullSource.RepoAt = &sourceRepoAtParsed ··· 422 382 pulls[pull.PullId] = &pull 423 383 } 424 384 425 - span.AddEvent("querying pull submissions") 426 - span.SetAttributes(attribute.Int("pull_count", len(pulls))) 427 - 428 385 // get latest round no. for each pull 429 386 inClause := strings.TrimSuffix(strings.Repeat("?, ", len(pulls)), ", ") 430 387 submissionsQuery := fmt.Sprintf(` ··· 443 400 args[idx] = p.PullId 444 401 idx += 1 445 402 } 446 - submissionsRows, err := e.QueryContext(ctx, submissionsQuery, args...) 403 + submissionsRows, err := e.Query(submissionsQuery, args...) 447 404 if err != nil { 448 - span.RecordError(err) 449 405 return nil, err 450 406 } 451 407 defer submissionsRows.Close() ··· 458 414 &s.RoundNumber, 459 415 ) 460 416 if err != nil { 461 - span.RecordError(err) 462 417 return nil, err 463 418 } 464 419 ··· 468 423 } 469 424 } 470 425 if err := rows.Err(); err != nil { 471 - span.RecordError(err) 472 426 return nil, err 473 427 } 474 - 475 - span.AddEvent("querying pull comments") 476 428 477 429 // get comment count on latest submission on each pull 478 430 inClause = strings.TrimSuffix(strings.Repeat("?, ", len(pulls)), ", ") ··· 491 443 for _, p := range pulls { 492 444 args = append(args, p.Submissions[p.LastRoundNumber()].ID) 493 445 } 494 - commentsRows, err := e.QueryContext(ctx, commentsQuery, args...) 446 + commentsRows, err := e.Query(commentsQuery, args...) 495 447 if err != nil { 496 - span.RecordError(err) 497 448 return nil, err 498 449 } 499 450 defer commentsRows.Close() ··· 505 456 &pullId, 506 457 ) 507 458 if err != nil { 508 - span.RecordError(err) 509 459 return nil, err 510 460 } 511 461 if p, ok := pulls[pullId]; ok { ··· 513 463 } 514 464 } 515 465 if err := rows.Err(); err != nil { 516 - span.RecordError(err) 517 466 return nil, err 518 467 } 519 - 520 - span.AddEvent("sorting pulls by date") 521 468 522 469 orderedByDate := []*Pull{} 523 470 for _, p := range pulls { ··· 527 474 return orderedByDate[i].Created.After(orderedByDate[j].Created) 528 475 }) 529 476 530 - span.SetAttributes(attribute.Int("result_count", len(orderedByDate))) 531 477 return orderedByDate, nil 532 478 } 533 479 534 - func GetPull(ctx context.Context, e Execer, repoAt syntax.ATURI, pullId int) (*Pull, error) { 535 - span := trace.SpanFromContext(ctx) 536 - defer span.End() 537 - 538 - span.SetAttributes(attribute.String("repoAt", repoAt.String()), attribute.Int("pull.id", pullId)) 539 - span.AddEvent("query pull metadata") 540 - 480 + func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*Pull, error) { 541 481 query := ` 542 482 select 543 483 owner_did, ··· 556 496 where 557 497 repo_at = ? and pull_id = ? 558 498 ` 559 - row := e.QueryRowContext(ctx, query, repoAt, pullId) 499 + row := e.QueryRow(query, repoAt, pullId) 560 500 561 501 var pull Pull 562 502 var createdAt string ··· 575 515 &sourceRepoAt, 576 516 ) 577 517 if err != nil { 578 - span.RecordError(err) 579 518 return nil, err 580 519 } 581 520 582 521 createdTime, err := time.Parse(time.RFC3339, createdAt) 583 522 if err != nil { 584 - span.RecordError(err) 585 523 return nil, err 586 524 } 587 525 pull.Created = createdTime 588 526 527 + // populate source 589 528 if sourceBranch.Valid { 590 529 pull.PullSource = &PullSource{ 591 530 Branch: sourceBranch.String, ··· 593 532 if sourceRepoAt.Valid { 594 533 sourceRepoAtParsed, err := syntax.ParseATURI(sourceRepoAt.String) 595 534 if err != nil { 596 - span.RecordError(err) 597 535 return nil, err 598 536 } 599 537 pull.PullSource.RepoAt = &sourceRepoAtParsed 600 538 } 601 539 } 602 540 603 - span.AddEvent("query submissions") 604 541 submissionsQuery := ` 605 542 select 606 543 id, pull_id, repo_at, round_number, patch, created, source_rev ··· 609 546 where 610 547 repo_at = ? and pull_id = ? 611 548 ` 612 - submissionsRows, err := e.QueryContext(ctx, submissionsQuery, repoAt, pullId) 549 + submissionsRows, err := e.Query(submissionsQuery, repoAt, pullId) 613 550 if err != nil { 614 - span.RecordError(err) 615 551 return nil, err 616 552 } 617 553 defer submissionsRows.Close() ··· 632 568 &submissionSourceRev, 633 569 ) 634 570 if err != nil { 635 - span.RecordError(err) 636 571 return nil, err 637 572 } 638 573 639 574 submissionCreatedTime, err := time.Parse(time.RFC3339, submissionCreatedStr) 640 575 if err != nil { 641 - span.RecordError(err) 642 576 return nil, err 643 577 } 644 578 submission.Created = submissionCreatedTime ··· 650 584 submissionsMap[submission.ID] = &submission 651 585 } 652 586 if err = submissionsRows.Close(); err != nil { 653 - span.RecordError(err) 654 587 return nil, err 655 588 } 656 589 if len(submissionsMap) == 0 { ··· 662 595 args = append(args, k) 663 596 } 664 597 inClause := strings.TrimSuffix(strings.Repeat("?, ", len(submissionsMap)), ", ") 665 - 666 - span.AddEvent("query comments") 667 598 commentsQuery := fmt.Sprintf(` 668 599 select 669 600 id, ··· 681 612 order by 682 613 created asc 683 614 `, inClause) 684 - commentsRows, err := e.QueryContext(ctx, commentsQuery, args...) 615 + commentsRows, err := e.Query(commentsQuery, args...) 685 616 if err != nil { 686 - span.RecordError(err) 687 617 return nil, err 688 618 } 689 619 defer commentsRows.Close() ··· 702 632 &commentCreatedStr, 703 633 ) 704 634 if err != nil { 705 - span.RecordError(err) 706 635 return nil, err 707 636 } 708 637 709 638 commentCreatedTime, err := time.Parse(time.RFC3339, commentCreatedStr) 710 639 if err != nil { 711 - span.RecordError(err) 712 640 return nil, err 713 641 } 714 642 comment.Created = commentCreatedTime 715 643 644 + // Add the comment to its submission 716 645 if submission, ok := submissionsMap[comment.SubmissionId]; ok { 717 646 submission.Comments = append(submission.Comments, comment) 718 647 } 648 + 719 649 } 720 650 if err = commentsRows.Err(); err != nil { 721 - span.RecordError(err) 722 651 return nil, err 723 652 } 724 653 725 - if pull.PullSource != nil && pull.PullSource.RepoAt != nil { 726 - span.AddEvent("query pull source repo") 727 - pullSourceRepo, err := GetRepoByAtUri(ctx, e, pull.PullSource.RepoAt.String()) 728 - if err != nil { 729 - span.RecordError(err) 730 - log.Printf("failed to get repo by at uri: %v", err) 731 - } else { 732 - pull.PullSource.Repo = pullSourceRepo 654 + var pullSourceRepo *Repo 655 + if pull.PullSource != nil { 656 + if pull.PullSource.RepoAt != nil { 657 + pullSourceRepo, err = GetRepoByAtUri(e, pull.PullSource.RepoAt.String()) 658 + if err != nil { 659 + log.Printf("failed to get repo by at uri: %v", err) 660 + } else { 661 + pull.PullSource.Repo = pullSourceRepo 662 + } 733 663 } 734 664 } 735 665 ··· 817 747 return pulls, nil 818 748 } 819 749 820 - func NewPullComment(ctx context.Context, e Execer, comment *PullComment) (int64, error) { 821 - span := trace.SpanFromContext(ctx) 822 - defer span.End() 823 - 824 - span.SetAttributes( 825 - attribute.String("repo.at", comment.RepoAt), 826 - attribute.Int("pull.id", comment.PullId), 827 - attribute.Int("submission.id", comment.SubmissionId), 828 - attribute.String("owner.did", comment.OwnerDid), 829 - ) 830 - span.AddEvent("inserting new pull comment") 831 - 750 + func NewPullComment(e Execer, comment *PullComment) (int64, error) { 832 751 query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 833 - res, err := e.ExecContext( 834 - ctx, 752 + res, err := e.Exec( 835 753 query, 836 754 comment.OwnerDid, 837 755 comment.RepoAt, ··· 841 759 comment.Body, 842 760 ) 843 761 if err != nil { 844 - span.RecordError(err) 845 762 return 0, err 846 763 } 847 764 848 765 i, err := res.LastInsertId() 849 766 if err != nil { 850 - span.RecordError(err) 851 767 return 0, err 852 768 } 853 769 854 - span.SetAttributes(attribute.Int64("comment.id", i)) 855 - span.AddEvent("pull comment created successfully") 856 770 return i, nil 857 771 } 858 772
+12 -114
appview/db/repos.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "database/sql" 6 5 "time" 7 6 8 7 "github.com/bluesky-social/indigo/atproto/syntax" 9 - "go.opentelemetry.io/otel" 10 - "go.opentelemetry.io/otel/attribute" 11 8 ) 12 9 13 10 type Repo struct { ··· 26 23 Source string 27 24 } 28 25 29 - func GetAllRepos(ctx context.Context, e Execer, limit int) ([]Repo, error) { 30 - ctx, span := otel.Tracer("db").Start(ctx, "GetAllRepos") 31 - defer span.End() 32 - span.SetAttributes(attribute.Int("limit", limit)) 33 - 26 + func GetAllRepos(e Execer, limit int) ([]Repo, error) { 34 27 var repos []Repo 35 28 36 29 rows, err := e.Query( ··· 42 35 limit, 43 36 ) 44 37 if err != nil { 45 - span.RecordError(err) 46 38 return nil, err 47 39 } 48 40 defer rows.Close() ··· 53 45 rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created, &repo.Source, 54 46 ) 55 47 if err != nil { 56 - span.RecordError(err) 57 48 return nil, err 58 49 } 59 50 repos = append(repos, repo) 60 51 } 61 52 62 53 if err := rows.Err(); err != nil { 63 - span.RecordError(err) 64 54 return nil, err 65 55 } 66 56 67 - span.SetAttributes(attribute.Int("repos.count", len(repos))) 68 57 return repos, nil 69 58 } 70 59 71 - func GetAllReposByDid(ctx context.Context, e Execer, did string) ([]Repo, error) { 72 - ctx, span := otel.Tracer("db").Start(ctx, "GetAllReposByDid") 73 - defer span.End() 74 - span.SetAttributes(attribute.String("did", did)) 75 - 60 + func GetAllReposByDid(e Execer, did string) ([]Repo, error) { 76 61 var repos []Repo 77 62 78 63 rows, err := e.Query( ··· 96 81 order by r.created desc`, 97 82 did) 98 83 if err != nil { 99 - span.RecordError(err) 100 84 return nil, err 101 85 } 102 86 defer rows.Close() ··· 110 94 111 95 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount, &nullableSource) 112 96 if err != nil { 113 - span.RecordError(err) 114 97 return nil, err 115 98 } 116 99 ··· 135 118 } 136 119 137 120 if err := rows.Err(); err != nil { 138 - span.RecordError(err) 139 121 return nil, err 140 122 } 141 123 142 - span.SetAttributes(attribute.Int("repos.count", len(repos))) 143 124 return repos, nil 144 125 } 145 126 146 - func GetRepo(ctx context.Context, e Execer, did, name string) (*Repo, error) { 147 - ctx, span := otel.Tracer("db").Start(ctx, "GetRepo") 148 - defer span.End() 149 - span.SetAttributes( 150 - attribute.String("did", did), 151 - attribute.String("name", name), 152 - ) 153 - 127 + func GetRepo(e Execer, did, name string) (*Repo, error) { 154 128 var repo Repo 155 129 var nullableDescription sql.NullString 156 130 ··· 158 132 159 133 var createdAt string 160 134 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 161 - span.RecordError(err) 162 135 return nil, err 163 136 } 164 137 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) ··· 173 146 return &repo, nil 174 147 } 175 148 176 - func GetRepoByAtUri(ctx context.Context, e Execer, atUri string) (*Repo, error) { 177 - ctx, span := otel.Tracer("db").Start(ctx, "GetRepoByAtUri") 178 - defer span.End() 179 - span.SetAttributes(attribute.String("atUri", atUri)) 180 - 149 + func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) { 181 150 var repo Repo 182 151 var nullableDescription sql.NullString 183 152 ··· 185 154 186 155 var createdAt string 187 156 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 188 - span.RecordError(err) 189 157 return nil, err 190 158 } 191 159 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) ··· 200 168 return &repo, nil 201 169 } 202 170 203 - func AddRepo(ctx context.Context, e Execer, repo *Repo) error { 204 - ctx, span := otel.Tracer("db").Start(ctx, "AddRepo") 205 - defer span.End() 206 - span.SetAttributes( 207 - attribute.String("did", repo.Did), 208 - attribute.String("name", repo.Name), 209 - ) 210 - 171 + func AddRepo(e Execer, repo *Repo) error { 211 172 _, err := e.Exec( 212 173 `insert into repos 213 174 (did, name, knot, rkey, at_uri, description, source) 214 175 values (?, ?, ?, ?, ?, ?, ?)`, 215 176 repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri, repo.Description, repo.Source, 216 177 ) 217 - if err != nil { 218 - span.RecordError(err) 219 - } 220 178 return err 221 179 } 222 180 223 - func RemoveRepo(ctx context.Context, e Execer, did, name string) error { 224 - ctx, span := otel.Tracer("db").Start(ctx, "RemoveRepo") 225 - defer span.End() 226 - span.SetAttributes( 227 - attribute.String("did", did), 228 - attribute.String("name", name), 229 - ) 230 - 181 + func RemoveRepo(e Execer, did, name string) error { 231 182 _, err := e.Exec(`delete from repos where did = ? and name = ?`, did, name) 232 - if err != nil { 233 - span.RecordError(err) 234 - } 235 183 return err 236 184 } 237 185 238 - func GetRepoSource(ctx context.Context, e Execer, repoAt syntax.ATURI) (string, error) { 239 - ctx, span := otel.Tracer("db").Start(ctx, "GetRepoSource") 240 - defer span.End() 241 - span.SetAttributes(attribute.String("repoAt", repoAt.String())) 242 - 186 + func GetRepoSource(e Execer, repoAt syntax.ATURI) (string, error) { 243 187 var nullableSource sql.NullString 244 188 err := e.QueryRow(`select source from repos where at_uri = ?`, repoAt).Scan(&nullableSource) 245 189 if err != nil { 246 - span.RecordError(err) 247 190 return "", err 248 191 } 249 192 return nullableSource.String, nil 250 193 } 251 194 252 - func GetForksByDid(ctx context.Context, e Execer, did string) ([]Repo, error) { 253 - ctx, span := otel.Tracer("db").Start(ctx, "GetForksByDid") 254 - defer span.End() 255 - span.SetAttributes(attribute.String("did", did)) 256 - 195 + func GetForksByDid(e Execer, did string) ([]Repo, error) { 257 196 var repos []Repo 258 197 259 198 rows, err := e.Query( ··· 264 203 did, 265 204 ) 266 205 if err != nil { 267 - span.RecordError(err) 268 206 return nil, err 269 207 } 270 208 defer rows.Close() ··· 277 215 278 216 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 279 217 if err != nil { 280 - span.RecordError(err) 281 218 return nil, err 282 219 } 283 220 ··· 300 237 } 301 238 302 239 if err := rows.Err(); err != nil { 303 - span.RecordError(err) 304 240 return nil, err 305 241 } 306 242 307 - span.SetAttributes(attribute.Int("forks.count", len(repos))) 308 243 return repos, nil 309 244 } 310 245 311 - func GetForkByDid(ctx context.Context, e Execer, did string, name string) (*Repo, error) { 312 - ctx, span := otel.Tracer("db").Start(ctx, "GetForkByDid") 313 - defer span.End() 314 - span.SetAttributes( 315 - attribute.String("did", did), 316 - attribute.String("name", name), 317 - ) 318 - 246 + func GetForkByDid(e Execer, did string, name string) (*Repo, error) { 319 247 var repo Repo 320 248 var createdAt string 321 249 var nullableDescription sql.NullString ··· 330 258 331 259 err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 332 260 if err != nil { 333 - span.RecordError(err) 334 261 return nil, err 335 262 } 336 263 ··· 352 279 return &repo, nil 353 280 } 354 281 355 - func AddCollaborator(ctx context.Context, e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error { 356 - ctx, span := otel.Tracer("db").Start(ctx, "AddCollaborator") 357 - defer span.End() 358 - span.SetAttributes( 359 - attribute.String("collaborator", collaborator), 360 - attribute.String("repoOwnerDid", repoOwnerDid), 361 - attribute.String("repoName", repoName), 362 - ) 363 - 282 + func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error { 364 283 _, err := e.Exec( 365 284 `insert into collaborators (did, repo) 366 285 values (?, (select id from repos where did = ? and name = ? and knot = ?));`, 367 286 collaborator, repoOwnerDid, repoName, repoKnot) 368 - if err != nil { 369 - span.RecordError(err) 370 - } 371 287 return err 372 288 } 373 289 374 - func UpdateDescription(ctx context.Context, e Execer, repoAt, newDescription string) error { 375 - ctx, span := otel.Tracer("db").Start(ctx, "UpdateDescription") 376 - defer span.End() 377 - span.SetAttributes( 378 - attribute.String("repoAt", repoAt), 379 - attribute.String("description", newDescription), 380 - ) 381 - 290 + func UpdateDescription(e Execer, repoAt, newDescription string) error { 382 291 _, err := e.Exec( 383 292 `update repos set description = ? where at_uri = ?`, newDescription, repoAt) 384 - if err != nil { 385 - span.RecordError(err) 386 - } 387 293 return err 388 294 } 389 295 390 - func CollaboratingIn(ctx context.Context, e Execer, collaborator string) ([]Repo, error) { 391 - ctx, span := otel.Tracer("db").Start(ctx, "CollaboratingIn") 392 - defer span.End() 393 - span.SetAttributes(attribute.String("collaborator", collaborator)) 394 - 296 + func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) { 395 297 var repos []Repo 396 298 397 299 rows, err := e.Query( ··· 408 310 group by 409 311 r.id;`, collaborator) 410 312 if err != nil { 411 - span.RecordError(err) 412 313 return nil, err 413 314 } 414 315 defer rows.Close() ··· 421 322 422 323 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount) 423 324 if err != nil { 424 - span.RecordError(err) 425 325 return nil, err 426 326 } 427 327 ··· 444 344 } 445 345 446 346 if err := rows.Err(); err != nil { 447 - span.RecordError(err) 448 347 return nil, err 449 348 } 450 349 451 - span.SetAttributes(attribute.Int("repos.count", len(repos))) 452 350 return repos, nil 453 351 } 454 352
+4 -5
appview/db/star.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "log" 6 5 "time" 7 6 ··· 18 17 Repo *Repo 19 18 } 20 19 21 - func (star *Star) ResolveRepo(ctx context.Context, e Execer) error { 20 + func (star *Star) ResolveRepo(e Execer) error { 22 21 if star.Repo != nil { 23 22 return nil 24 23 } 25 24 26 - repo, err := GetRepoByAtUri(ctx, e, star.RepoAt.String()) 25 + repo, err := GetRepoByAtUri(e, star.RepoAt.String()) 27 26 if err != nil { 28 27 return err 29 28 } ··· 41 40 // Get a star record 42 41 func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*Star, error) { 43 42 query := ` 44 - select starred_by_did, repo_at, created, rkey 43 + select starred_by_did, repo_at, created, rkey 45 44 from stars 46 45 where starred_by_did = ? and repo_at = ?` 47 46 row := e.QueryRow(query, starredByDid, repoAt) ··· 98 97 var stars []Star 99 98 100 99 rows, err := e.Query(` 101 - select 100 + select 102 101 s.starred_by_did, 103 102 s.repo_at, 104 103 s.rkey,
+3 -28
appview/db/timeline.go
··· 1 1 package db 2 2 3 3 import ( 4 - "context" 5 4 "sort" 6 5 "time" 7 - 8 - "go.opentelemetry.io/otel/attribute" 9 - "go.opentelemetry.io/otel/trace" 10 6 ) 11 7 12 8 type TimelineEvent struct { ··· 22 18 23 19 // TODO: this gathers heterogenous events from different sources and aggregates 24 20 // them in code; if we did this entirely in sql, we could order and limit and paginate easily 25 - func MakeTimeline(ctx context.Context, e Execer) ([]TimelineEvent, error) { 26 - span := trace.SpanFromContext(ctx) 27 - defer span.End() 28 - 21 + func MakeTimeline(e Execer) ([]TimelineEvent, error) { 29 22 var events []TimelineEvent 30 23 limit := 50 31 24 32 - span.SetAttributes(attribute.Int("timeline.limit", limit)) 33 - 34 - repos, err := GetAllRepos(ctx, e, limit) 25 + repos, err := GetAllRepos(e, limit) 35 26 if err != nil { 36 - span.RecordError(err) 37 - span.SetAttributes(attribute.String("error.from", "GetAllRepos")) 38 27 return nil, err 39 28 } 40 - span.SetAttributes(attribute.Int("timeline.repos.count", len(repos))) 41 29 42 30 follows, err := GetAllFollows(e, limit) 43 31 if err != nil { 44 - span.RecordError(err) 45 - span.SetAttributes(attribute.String("error.from", "GetAllFollows")) 46 32 return nil, err 47 33 } 48 - span.SetAttributes(attribute.Int("timeline.follows.count", len(follows))) 49 34 50 35 stars, err := GetAllStars(e, limit) 51 36 if err != nil { 52 - span.RecordError(err) 53 - span.SetAttributes(attribute.String("error.from", "GetAllStars")) 54 37 return nil, err 55 38 } 56 - span.SetAttributes(attribute.Int("timeline.stars.count", len(stars))) 57 39 58 40 for _, repo := range repos { 59 41 var sourceRepo *Repo 60 42 if repo.Source != "" { 61 - sourceRepo, err = GetRepoByAtUri(ctx, e, repo.Source) 43 + sourceRepo, err = GetRepoByAtUri(e, repo.Source) 62 44 if err != nil { 63 - span.RecordError(err) 64 - span.SetAttributes( 65 - attribute.String("error.from", "GetRepoByAtUri"), 66 - attribute.String("repo.source", repo.Source), 67 - ) 68 45 return nil, err 69 46 } 70 47 } ··· 98 75 if len(events) > limit { 99 76 events = events[:limit] 100 77 } 101 - 102 - span.SetAttributes(attribute.Int("timeline.events.total", len(events))) 103 78 104 79 return events, nil 105 80 }
+1 -1
appview/state/artifact.go
··· 118 118 119 119 s.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{ 120 120 LoggedInUser: user, 121 - RepoInfo: f.RepoInfo(r.Context(), s, user), 121 + RepoInfo: f.RepoInfo(s, user), 122 122 Artifact: artifact, 123 123 }) 124 124 }
+13 -31
appview/state/middleware.go
··· 12 12 13 13 "github.com/bluesky-social/indigo/atproto/identity" 14 14 "github.com/go-chi/chi/v5" 15 - "go.opentelemetry.io/otel/attribute" 16 15 "tangled.sh/tangled.sh/core/appview/db" 17 16 "tangled.sh/tangled.sh/core/appview/middleware" 18 17 ) ··· 20 19 func knotRoleMiddleware(s *State, group string) middleware.Middleware { 21 20 return func(next http.Handler) http.Handler { 22 21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 - ctx, span := s.t.TraceStart(r.Context(), "knotRoleMiddleware") 24 - defer span.End() 25 - 26 22 // requires auth also 27 - actor := s.auth.GetUser(r.WithContext(ctx)) 23 + actor := s.auth.GetUser(r) 28 24 if actor == nil { 29 25 // we need a logged in user 30 26 log.Printf("not logged in, redirecting") ··· 45 41 return 46 42 } 47 43 48 - next.ServeHTTP(w, r.WithContext(ctx)) 44 + next.ServeHTTP(w, r) 49 45 }) 50 46 } 51 47 } ··· 57 53 func RepoPermissionMiddleware(s *State, requiredPerm string) middleware.Middleware { 58 54 return func(next http.Handler) http.Handler { 59 55 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 60 - ctx, span := s.t.TraceStart(r.Context(), "RepoPermissionMiddleware") 61 - defer span.End() 62 - 63 56 // requires auth also 64 - actor := s.auth.GetUser(r.WithContext(ctx)) 57 + actor := s.auth.GetUser(r) 65 58 if actor == nil { 66 59 // we need a logged in user 67 60 log.Printf("not logged in, redirecting") 68 61 http.Error(w, "Forbiden", http.StatusUnauthorized) 69 62 return 70 63 } 71 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 64 + f, err := s.fullyResolvedRepo(r) 72 65 if err != nil { 73 66 http.Error(w, "malformed url", http.StatusBadRequest) 74 67 return ··· 82 75 return 83 76 } 84 77 85 - next.ServeHTTP(w, r.WithContext(ctx)) 78 + next.ServeHTTP(w, r) 86 79 }) 87 80 } 88 81 } ··· 108 101 return 109 102 } 110 103 111 - ctx, span := s.t.TraceStart(req.Context(), "ResolveIdent") 112 - defer span.End() 113 - 114 - id, err := s.resolver.ResolveIdent(ctx, didOrHandle) 104 + id, err := s.resolver.ResolveIdent(req.Context(), didOrHandle) 115 105 if err != nil { 116 106 // invalid did or handle 117 107 log.Println("failed to resolve did/handle:", err) ··· 119 109 return 120 110 } 121 111 122 - ctx = context.WithValue(ctx, "resolvedId", *id) 112 + ctx := context.WithValue(req.Context(), "resolvedId", *id) 123 113 124 114 next.ServeHTTP(w, req.WithContext(ctx)) 125 115 }) ··· 129 119 func ResolveRepo(s *State) middleware.Middleware { 130 120 return func(next http.Handler) http.Handler { 131 121 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 132 - ctx, span := s.t.TraceStart(req.Context(), "ResolveRepo") 133 - defer span.End() 134 - 135 122 repoName := chi.URLParam(req, "repo") 136 - id, ok := ctx.Value("resolvedId").(identity.Identity) 123 + id, ok := req.Context().Value("resolvedId").(identity.Identity) 137 124 if !ok { 138 125 log.Println("malformed middleware") 139 126 w.WriteHeader(http.StatusInternalServerError) 140 127 return 141 128 } 142 129 143 - repo, err := db.GetRepo(ctx, s.db, id.DID.String(), repoName) 130 + repo, err := db.GetRepo(s.db, id.DID.String(), repoName) 144 131 if err != nil { 145 132 // invalid did or handle 146 133 log.Println("failed to resolve repo") ··· 148 135 return 149 136 } 150 137 151 - ctx = context.WithValue(ctx, "knot", repo.Knot) 138 + ctx := context.WithValue(req.Context(), "knot", repo.Knot) 152 139 ctx = context.WithValue(ctx, "repoAt", repo.AtUri) 153 140 ctx = context.WithValue(ctx, "repoDescription", repo.Description) 154 141 ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339)) ··· 161 148 func ResolvePull(s *State) middleware.Middleware { 162 149 return func(next http.Handler) http.Handler { 163 150 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 164 - ctx, span := s.t.TraceStart(r.Context(), "ResolvePull") 165 - defer span.End() 166 - 167 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 151 + f, err := s.fullyResolvedRepo(r) 168 152 if err != nil { 169 153 log.Println("failed to fully resolve repo", err) 170 154 http.Error(w, "invalid repo url", http.StatusNotFound) ··· 179 163 return 180 164 } 181 165 182 - pr, err := db.GetPull(ctx, s.db, f.RepoAt, prIdInt) 166 + pr, err := db.GetPull(s.db, f.RepoAt, prIdInt) 183 167 if err != nil { 184 168 log.Println("failed to get pull and comments", err) 185 169 return 186 170 } 187 171 188 - span.SetAttributes(attribute.Int("pull.id", prIdInt)) 189 - 190 - ctx = context.WithValue(ctx, "pull", pr) 172 + ctx := context.WithValue(r.Context(), "pull", pr) 191 173 192 174 next.ServeHTTP(w, r.WithContext(ctx)) 193 175 })
+5 -33
appview/state/profile.go
··· 10 10 11 11 "github.com/bluesky-social/indigo/atproto/identity" 12 12 "github.com/go-chi/chi/v5" 13 - "go.opentelemetry.io/otel/attribute" 14 13 "tangled.sh/tangled.sh/core/appview/db" 15 14 "tangled.sh/tangled.sh/core/appview/pages" 16 15 ) 17 16 18 17 func (s *State) ProfilePage(w http.ResponseWriter, r *http.Request) { 19 - ctx, span := s.t.TraceStart(r.Context(), "ProfilePage") 20 - defer span.End() 21 - 22 18 didOrHandle := chi.URLParam(r, "user") 23 19 if didOrHandle == "" { 24 20 http.Error(w, "Bad request", http.StatusBadRequest) 25 21 return 26 22 } 27 23 28 - ident, ok := ctx.Value("resolvedId").(identity.Identity) 24 + ident, ok := r.Context().Value("resolvedId").(identity.Identity) 29 25 if !ok { 30 26 s.pages.Error404(w) 31 - span.RecordError(fmt.Errorf("failed to resolve identity")) 32 27 return 33 28 } 34 29 35 - span.SetAttributes( 36 - attribute.String("user.did", ident.DID.String()), 37 - attribute.String("user.handle", ident.Handle.String()), 38 - ) 39 - 40 - repos, err := db.GetAllReposByDid(ctx, s.db, ident.DID.String()) 30 + repos, err := db.GetAllReposByDid(s.db, ident.DID.String()) 41 31 if err != nil { 42 32 log.Printf("getting repos for %s: %s", ident.DID.String(), err) 43 - span.RecordError(err) 44 - span.SetAttributes(attribute.String("error.repos", err.Error())) 45 33 } 46 - span.SetAttributes(attribute.Int("repos.count", len(repos))) 47 34 48 - collaboratingRepos, err := db.CollaboratingIn(ctx, s.db, ident.DID.String()) 35 + collaboratingRepos, err := db.CollaboratingIn(s.db, ident.DID.String()) 49 36 if err != nil { 50 37 log.Printf("getting collaborating repos for %s: %s", ident.DID.String(), err) 51 - span.RecordError(err) 52 - span.SetAttributes(attribute.String("error.collaborating_repos", err.Error())) 53 38 } 54 - span.SetAttributes(attribute.Int("collaborating_repos.count", len(collaboratingRepos))) 55 39 56 - timeline, err := db.MakeProfileTimeline(ctx, s.db, ident.DID.String()) 40 + timeline, err := db.MakeProfileTimeline(s.db, ident.DID.String()) 57 41 if err != nil { 58 42 log.Printf("failed to create profile timeline for %s: %s", ident.DID.String(), err) 59 - span.RecordError(err) 60 - span.SetAttributes(attribute.String("error.timeline", err.Error())) 61 43 } 62 44 63 45 var didsToResolve []string ··· 78 60 } 79 61 } 80 62 } 81 - span.SetAttributes(attribute.Int("dids_to_resolve.count", len(didsToResolve))) 82 63 83 - resolvedIds := s.resolver.ResolveIdents(ctx, didsToResolve) 64 + resolvedIds := s.resolver.ResolveIdents(r.Context(), didsToResolve) 84 65 didHandleMap := make(map[string]string) 85 66 for _, identity := range resolvedIds { 86 67 if !identity.Handle.IsInvalidHandle() { ··· 89 70 didHandleMap[identity.DID.String()] = identity.DID.String() 90 71 } 91 72 } 92 - span.SetAttributes(attribute.Int("resolved_ids.count", len(resolvedIds))) 93 73 94 74 followers, following, err := db.GetFollowerFollowing(s.db, ident.DID.String()) 95 75 if err != nil { 96 76 log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err) 97 - span.RecordError(err) 98 - span.SetAttributes(attribute.String("error.follow_stats", err.Error())) 99 77 } 100 - span.SetAttributes( 101 - attribute.Int("followers.count", followers), 102 - attribute.Int("following.count", following), 103 - ) 104 78 105 79 loggedInUser := s.auth.GetUser(r) 106 80 followStatus := db.IsNotFollowing 107 81 if loggedInUser != nil { 108 82 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) 109 - span.SetAttributes(attribute.String("logged_in_user.did", loggedInUser.Did)) 110 83 } 111 - span.SetAttributes(attribute.String("follow_status", string(db.FollowStatus(followStatus)))) 112 84 113 85 profileAvatarUri := s.GetAvatarUri(ident.Handle.String()) 114 86 s.pages.ProfilePage(w, pages.ProfilePageParams{
+125 -633
appview/state/pull.go
··· 1 1 package state 2 2 3 3 import ( 4 - "context" 5 4 "database/sql" 6 5 "encoding/json" 7 6 "errors" ··· 12 11 "strconv" 13 12 "time" 14 13 15 - "go.opentelemetry.io/otel/attribute" 16 14 "tangled.sh/tangled.sh/core/api/tangled" 17 15 "tangled.sh/tangled.sh/core/appview" 18 16 "tangled.sh/tangled.sh/core/appview/auth" 19 17 "tangled.sh/tangled.sh/core/appview/db" 20 18 "tangled.sh/tangled.sh/core/appview/pages" 21 19 "tangled.sh/tangled.sh/core/patchutil" 22 - "tangled.sh/tangled.sh/core/telemetry" 23 20 "tangled.sh/tangled.sh/core/types" 24 21 25 22 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 30 27 31 28 // htmx fragment 32 29 func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { 33 - ctx, span := s.t.TraceStart(r.Context(), "PullActions") 34 - defer span.End() 35 - 36 30 switch r.Method { 37 31 case http.MethodGet: 38 32 user := s.auth.GetUser(r) 39 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 33 + f, err := s.fullyResolvedRepo(r) 40 34 if err != nil { 41 35 log.Println("failed to get repo and knot", err) 42 36 return 43 37 } 44 38 45 - pull, ok := ctx.Value("pull").(*db.Pull) 39 + pull, ok := r.Context().Value("pull").(*db.Pull) 46 40 if !ok { 47 41 log.Println("failed to get pull") 48 42 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") ··· 60 54 return 61 55 } 62 56 63 - _, mergeSpan := s.t.TraceStart(ctx, "mergeCheck") 64 - mergeCheckResponse := s.mergeCheck(ctx, f, pull) 65 - mergeSpan.End() 66 - 57 + mergeCheckResponse := s.mergeCheck(f, pull) 67 58 resubmitResult := pages.Unknown 68 59 if user.Did == pull.OwnerDid { 69 - _, resubmitSpan := s.t.TraceStart(ctx, "resubmitCheck") 70 - resubmitResult = s.resubmitCheck(ctx, f, pull) 71 - resubmitSpan.End() 60 + resubmitResult = s.resubmitCheck(f, pull) 72 61 } 73 62 74 - _, renderSpan := s.t.TraceStart(ctx, "renderPullActions") 75 63 s.pages.PullActionsFragment(w, pages.PullActionsParams{ 76 64 LoggedInUser: user, 77 - RepoInfo: f.RepoInfo(ctx, s, user), 65 + RepoInfo: f.RepoInfo(s, user), 78 66 Pull: pull, 79 67 RoundNumber: roundNumber, 80 68 MergeCheck: mergeCheckResponse, 81 69 ResubmitCheck: resubmitResult, 82 70 }) 83 - renderSpan.End() 84 71 return 85 72 } 86 73 } 87 74 88 75 func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { 89 - ctx, span := s.t.TraceStart(r.Context(), "RepoSinglePull") 90 - defer span.End() 91 - 92 76 user := s.auth.GetUser(r) 93 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 77 + f, err := s.fullyResolvedRepo(r) 94 78 if err != nil { 95 79 log.Println("failed to get repo and knot", err) 96 - span.RecordError(err) 97 80 return 98 81 } 99 82 100 - pull, ok := ctx.Value("pull").(*db.Pull) 83 + pull, ok := r.Context().Value("pull").(*db.Pull) 101 84 if !ok { 102 - err := errors.New("failed to get pull from context") 103 - log.Println(err) 104 - span.RecordError(err) 85 + log.Println("failed to get pull") 105 86 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 106 87 return 107 88 } 108 89 109 - attrs := telemetry.MapAttrs[string](map[string]string{ 110 - "pull.id": fmt.Sprintf("%d", pull.PullId), 111 - "pull.owner": pull.OwnerDid, 112 - }) 113 - 114 - span.SetAttributes(attrs...) 115 - 116 90 totalIdents := 1 117 91 for _, submission := range pull.Submissions { 118 92 totalIdents += len(submission.Comments) ··· 130 104 } 131 105 } 132 106 133 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 107 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 134 108 didHandleMap := make(map[string]string) 135 109 for _, identity := range resolvedIds { 136 110 if !identity.Handle.IsInvalidHandle() { ··· 139 113 didHandleMap[identity.DID.String()] = identity.DID.String() 140 114 } 141 115 } 142 - span.SetAttributes(attribute.Int("identities.resolved", len(resolvedIds))) 143 116 144 - mergeCheckResponse := s.mergeCheck(ctx, f, pull) 145 - 117 + mergeCheckResponse := s.mergeCheck(f, pull) 146 118 resubmitResult := pages.Unknown 147 119 if user != nil && user.Did == pull.OwnerDid { 148 - resubmitResult = s.resubmitCheck(ctx, f, pull) 120 + resubmitResult = s.resubmitCheck(f, pull) 149 121 } 150 122 151 123 s.pages.RepoSinglePull(w, pages.RepoSinglePullParams{ 152 124 LoggedInUser: user, 153 - RepoInfo: f.RepoInfo(ctx, s, user), 125 + RepoInfo: f.RepoInfo(s, user), 154 126 DidHandleMap: didHandleMap, 155 127 Pull: pull, 156 128 MergeCheck: mergeCheckResponse, ··· 158 130 }) 159 131 } 160 132 161 - func (s *State) mergeCheck(ctx context.Context, f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse { 133 + func (s *State) mergeCheck(f *FullyResolvedRepo, pull *db.Pull) types.MergeCheckResponse { 162 134 if pull.State == db.PullMerged { 163 135 return types.MergeCheckResponse{} 164 136 } ··· 218 190 return mergeCheckResponse 219 191 } 220 192 221 - func (s *State) resubmitCheck(ctx context.Context, f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult { 222 - ctx, span := s.t.TraceStart(ctx, "resubmitCheck") 223 - defer span.End() 224 - 225 - span.SetAttributes(attribute.Int("pull.id", pull.PullId)) 226 - 193 + func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.ResubmitResult { 227 194 if pull.State == db.PullMerged || pull.PullSource == nil { 228 - span.SetAttributes(attribute.String("result", "Unknown")) 229 195 return pages.Unknown 230 196 } 231 197 ··· 233 199 234 200 if pull.PullSource.RepoAt != nil { 235 201 // fork-based pulls 236 - span.SetAttributes(attribute.Bool("isForkBased", true)) 237 - sourceRepo, err := db.GetRepoByAtUri(ctx, s.db, pull.PullSource.RepoAt.String()) 202 + sourceRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String()) 238 203 if err != nil { 239 204 log.Println("failed to get source repo", err) 240 - span.RecordError(err) 241 - span.SetAttributes(attribute.String("error", "failed_to_get_source_repo")) 242 - span.SetAttributes(attribute.String("result", "Unknown")) 243 205 return pages.Unknown 244 206 } 245 207 ··· 248 210 repoName = sourceRepo.Name 249 211 } else { 250 212 // pulls within the same repo 251 - span.SetAttributes(attribute.Bool("isBranchBased", true)) 252 213 knot = f.Knot 253 214 ownerDid = f.OwnerDid() 254 215 repoName = f.RepoName 255 216 } 256 217 257 - span.SetAttributes( 258 - attribute.String("knot", knot), 259 - attribute.String("ownerDid", ownerDid), 260 - attribute.String("repoName", repoName), 261 - attribute.String("sourceBranch", pull.PullSource.Branch), 262 - ) 263 - 264 218 us, err := NewUnsignedClient(knot, s.config.Dev) 265 219 if err != nil { 266 220 log.Printf("failed to setup client for %s; ignoring: %v", knot, err) 267 - span.RecordError(err) 268 - span.SetAttributes(attribute.String("error", "failed_to_setup_client")) 269 - span.SetAttributes(attribute.String("result", "Unknown")) 270 221 return pages.Unknown 271 222 } 272 223 273 224 resp, err := us.Branch(ownerDid, repoName, pull.PullSource.Branch) 274 225 if err != nil { 275 226 log.Println("failed to reach knotserver", err) 276 - span.RecordError(err) 277 - span.SetAttributes(attribute.String("error", "failed_to_reach_knotserver")) 278 - span.SetAttributes(attribute.String("result", "Unknown")) 279 227 return pages.Unknown 280 228 } 281 229 282 230 body, err := io.ReadAll(resp.Body) 283 231 if err != nil { 284 232 log.Printf("error reading response body: %v", err) 285 - span.RecordError(err) 286 - span.SetAttributes(attribute.String("error", "failed_to_read_response")) 287 - span.SetAttributes(attribute.String("result", "Unknown")) 288 233 return pages.Unknown 289 234 } 290 235 defer resp.Body.Close() ··· 292 237 var result types.RepoBranchResponse 293 238 if err := json.Unmarshal(body, &result); err != nil { 294 239 log.Println("failed to parse response:", err) 295 - span.RecordError(err) 296 - span.SetAttributes(attribute.String("error", "failed_to_parse_response")) 297 - span.SetAttributes(attribute.String("result", "Unknown")) 298 240 return pages.Unknown 299 241 } 300 242 301 243 latestSubmission := pull.Submissions[pull.LastRoundNumber()] 302 - 303 - span.SetAttributes( 304 - attribute.String("latestSubmission.SourceRev", latestSubmission.SourceRev), 305 - attribute.String("branch.Hash", result.Branch.Hash), 306 - ) 307 - 308 244 if latestSubmission.SourceRev != result.Branch.Hash { 309 245 fmt.Println(latestSubmission.SourceRev, result.Branch.Hash) 310 - span.SetAttributes(attribute.String("result", "ShouldResubmit")) 311 246 return pages.ShouldResubmit 312 247 } 313 248 314 - span.SetAttributes(attribute.String("result", "ShouldNotResubmit")) 315 249 return pages.ShouldNotResubmit 316 250 } 317 251 318 252 func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { 319 - ctx, span := s.t.TraceStart(r.Context(), "RepoPullPatch") 320 - defer span.End() 321 - 322 - user := s.auth.GetUser(r.WithContext(ctx)) 323 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 253 + user := s.auth.GetUser(r) 254 + f, err := s.fullyResolvedRepo(r) 324 255 if err != nil { 325 256 log.Println("failed to get repo and knot", err) 326 - span.RecordError(err) 327 257 return 328 258 } 329 259 330 - pull, ok := ctx.Value("pull").(*db.Pull) 260 + pull, ok := r.Context().Value("pull").(*db.Pull) 331 261 if !ok { 332 - err := errors.New("failed to get pull from context") 333 - log.Println(err) 334 - span.RecordError(err) 262 + log.Println("failed to get pull") 335 263 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 336 264 return 337 265 } ··· 341 269 if err != nil || roundIdInt >= len(pull.Submissions) { 342 270 http.Error(w, "bad round id", http.StatusBadRequest) 343 271 log.Println("failed to parse round id", err) 344 - span.RecordError(err) 345 - span.SetAttributes(attribute.String("error", "bad_round_id")) 346 272 return 347 273 } 348 274 349 - span.SetAttributes( 350 - attribute.Int("pull.id", pull.PullId), 351 - attribute.Int("round", roundIdInt), 352 - attribute.String("pull.owner", pull.OwnerDid), 353 - ) 354 - 355 275 identsToResolve := []string{pull.OwnerDid} 356 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 276 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 357 277 didHandleMap := make(map[string]string) 358 278 for _, identity := range resolvedIds { 359 279 if !identity.Handle.IsInvalidHandle() { ··· 362 282 didHandleMap[identity.DID.String()] = identity.DID.String() 363 283 } 364 284 } 365 - span.SetAttributes(attribute.Int("identities.resolved", len(resolvedIds))) 366 285 367 286 diff := pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch) 368 287 369 288 s.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{ 370 289 LoggedInUser: user, 371 290 DidHandleMap: didHandleMap, 372 - RepoInfo: f.RepoInfo(ctx, s, user), 291 + RepoInfo: f.RepoInfo(s, user), 373 292 Pull: pull, 374 293 Round: roundIdInt, 375 294 Submission: pull.Submissions[roundIdInt], 376 295 Diff: &diff, 377 296 }) 297 + 378 298 } 379 299 380 300 func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) { 381 - ctx, span := s.t.TraceStart(r.Context(), "RepoPullInterdiff") 382 - defer span.End() 383 - 384 301 user := s.auth.GetUser(r) 385 302 386 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 303 + f, err := s.fullyResolvedRepo(r) 387 304 if err != nil { 388 305 log.Println("failed to get repo and knot", err) 389 306 return 390 307 } 391 308 392 - pull, ok := ctx.Value("pull").(*db.Pull) 309 + pull, ok := r.Context().Value("pull").(*db.Pull) 393 310 if !ok { 394 311 log.Println("failed to get pull") 395 312 s.pages.Notice(w, "pull-error", "Failed to get pull.") 396 313 return 397 314 } 398 315 399 - _, roundSpan := s.t.TraceStart(ctx, "parseRound") 400 316 roundId := chi.URLParam(r, "round") 401 317 roundIdInt, err := strconv.Atoi(roundId) 402 318 if err != nil || roundIdInt >= len(pull.Submissions) { 403 319 http.Error(w, "bad round id", http.StatusBadRequest) 404 320 log.Println("failed to parse round id", err) 405 - roundSpan.End() 406 321 return 407 322 } 408 323 409 324 if roundIdInt == 0 { 410 325 http.Error(w, "bad round id", http.StatusBadRequest) 411 326 log.Println("cannot interdiff initial submission") 412 - roundSpan.End() 413 327 return 414 328 } 415 - roundSpan.End() 416 329 417 - _, identSpan := s.t.TraceStart(ctx, "resolveIdentities") 418 330 identsToResolve := []string{pull.OwnerDid} 419 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 331 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 420 332 didHandleMap := make(map[string]string) 421 333 for _, identity := range resolvedIds { 422 334 if !identity.Handle.IsInvalidHandle() { ··· 425 337 didHandleMap[identity.DID.String()] = identity.DID.String() 426 338 } 427 339 } 428 - identSpan.End() 429 340 430 - _, diffSpan := s.t.TraceStart(ctx, "calculateInterdiff") 431 341 currentPatch, err := pull.Submissions[roundIdInt].AsDiff(pull.TargetBranch) 432 342 if err != nil { 433 343 log.Println("failed to interdiff; current patch malformed") 434 344 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; current patch is invalid.") 435 - diffSpan.End() 436 345 return 437 346 } 438 347 ··· 440 349 if err != nil { 441 350 log.Println("failed to interdiff; previous patch malformed") 442 351 s.pages.Notice(w, fmt.Sprintf("interdiff-error-%d", roundIdInt), "Failed to calculate interdiff; previous patch is invalid.") 443 - diffSpan.End() 444 352 return 445 353 } 446 354 447 355 interdiff := patchutil.Interdiff(previousPatch, currentPatch) 448 - diffSpan.End() 449 356 450 - _, renderSpan := s.t.TraceStart(ctx, "renderInterdiffPage") 451 357 s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{ 452 - LoggedInUser: s.auth.GetUser(r.WithContext(ctx)), 453 - RepoInfo: f.RepoInfo(ctx, s, user), 358 + LoggedInUser: s.auth.GetUser(r), 359 + RepoInfo: f.RepoInfo(s, user), 454 360 Pull: pull, 455 361 Round: roundIdInt, 456 362 DidHandleMap: didHandleMap, 457 363 Interdiff: interdiff, 458 364 }) 459 - renderSpan.End() 460 365 return 461 366 } 462 367 463 368 func (s *State) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) { 464 - ctx, span := s.t.TraceStart(r.Context(), "RepoPullPatchRaw") 465 - defer span.End() 466 - 467 - pull, ok := ctx.Value("pull").(*db.Pull) 369 + pull, ok := r.Context().Value("pull").(*db.Pull) 468 370 if !ok { 469 371 log.Println("failed to get pull") 470 372 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 471 373 return 472 374 } 473 375 474 - _, roundSpan := s.t.TraceStart(ctx, "parseRound") 475 376 roundId := chi.URLParam(r, "round") 476 377 roundIdInt, err := strconv.Atoi(roundId) 477 378 if err != nil || roundIdInt >= len(pull.Submissions) { 478 379 http.Error(w, "bad round id", http.StatusBadRequest) 479 380 log.Println("failed to parse round id", err) 480 - roundSpan.End() 481 381 return 482 382 } 483 - roundSpan.End() 484 383 485 - _, identSpan := s.t.TraceStart(ctx, "resolveIdentities") 486 384 identsToResolve := []string{pull.OwnerDid} 487 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 385 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 488 386 didHandleMap := make(map[string]string) 489 387 for _, identity := range resolvedIds { 490 388 if !identity.Handle.IsInvalidHandle() { ··· 493 391 didHandleMap[identity.DID.String()] = identity.DID.String() 494 392 } 495 393 } 496 - identSpan.End() 497 394 498 - _, writeSpan := s.t.TraceStart(ctx, "writePatch") 499 395 w.Header().Set("Content-Type", "text/plain") 500 396 w.Write([]byte(pull.Submissions[roundIdInt].Patch)) 501 - writeSpan.End() 502 397 } 503 398 504 399 func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { 505 - ctx, span := s.t.TraceStart(r.Context(), "RepoPulls") 506 - defer span.End() 507 - 508 400 user := s.auth.GetUser(r) 509 401 params := r.URL.Query() 510 402 511 - _, stateSpan := s.t.TraceStart(ctx, "determinePullState") 512 403 state := db.PullOpen 513 404 switch params.Get("state") { 514 405 case "closed": ··· 516 407 case "merged": 517 408 state = db.PullMerged 518 409 } 519 - stateSpan.End() 520 410 521 - _, repoSpan := s.t.TraceStart(ctx, "resolveRepo") 522 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 411 + f, err := s.fullyResolvedRepo(r) 523 412 if err != nil { 524 413 log.Println("failed to get repo and knot", err) 525 - repoSpan.End() 526 414 return 527 415 } 528 - repoSpan.End() 529 416 530 - _, pullsSpan := s.t.TraceStart(ctx, "getPulls") 531 - pulls, err := db.GetPulls(ctx, s.db, f.RepoAt, state) 417 + pulls, err := db.GetPulls(s.db, f.RepoAt, state) 532 418 if err != nil { 533 419 log.Println("failed to get pulls", err) 534 420 s.pages.Notice(w, "pulls", "Failed to load pulls. Try again later.") 535 - pullsSpan.End() 536 421 return 537 422 } 538 - pullsSpan.End() 539 423 540 - _, sourceRepoSpan := s.t.TraceStart(ctx, "resolvePullSources") 541 424 for _, p := range pulls { 542 425 var pullSourceRepo *db.Repo 543 426 if p.PullSource != nil { 544 427 if p.PullSource.RepoAt != nil { 545 - pullSourceRepo, err = db.GetRepoByAtUri(ctx, s.db, p.PullSource.RepoAt.String()) 428 + pullSourceRepo, err = db.GetRepoByAtUri(s.db, p.PullSource.RepoAt.String()) 546 429 if err != nil { 547 430 log.Printf("failed to get repo by at uri: %v", err) 548 431 continue ··· 552 435 } 553 436 } 554 437 } 555 - sourceRepoSpan.End() 556 438 557 - _, identSpan := s.t.TraceStart(ctx, "resolveIdentities") 558 439 identsToResolve := make([]string, len(pulls)) 559 440 for i, pull := range pulls { 560 441 identsToResolve[i] = pull.OwnerDid 561 442 } 562 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 443 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 563 444 didHandleMap := make(map[string]string) 564 445 for _, identity := range resolvedIds { 565 446 if !identity.Handle.IsInvalidHandle() { ··· 568 449 didHandleMap[identity.DID.String()] = identity.DID.String() 569 450 } 570 451 } 571 - identSpan.End() 572 452 573 - _, renderSpan := s.t.TraceStart(ctx, "renderPullsPage") 574 453 s.pages.RepoPulls(w, pages.RepoPullsParams{ 575 - LoggedInUser: s.auth.GetUser(r.WithContext(ctx)), 576 - RepoInfo: f.RepoInfo(ctx, s, user), 454 + LoggedInUser: s.auth.GetUser(r), 455 + RepoInfo: f.RepoInfo(s, user), 577 456 Pulls: pulls, 578 457 DidHandleMap: didHandleMap, 579 458 FilteringBy: state, 580 459 }) 581 - renderSpan.End() 582 460 return 583 461 } 584 462 585 463 func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { 586 - ctx, span := s.t.TraceStart(r.Context(), "PullComment") 587 - defer span.End() 588 - 589 - user := s.auth.GetUser(r.WithContext(ctx)) 590 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 464 + user := s.auth.GetUser(r) 465 + f, err := s.fullyResolvedRepo(r) 591 466 if err != nil { 592 467 log.Println("failed to get repo and knot", err) 593 468 return 594 469 } 595 470 596 - pull, ok := ctx.Value("pull").(*db.Pull) 471 + pull, ok := r.Context().Value("pull").(*db.Pull) 597 472 if !ok { 598 473 log.Println("failed to get pull") 599 474 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 600 475 return 601 476 } 602 477 603 - _, roundSpan := s.t.TraceStart(ctx, "parseRoundNumber") 604 478 roundNumberStr := chi.URLParam(r, "round") 605 479 roundNumber, err := strconv.Atoi(roundNumberStr) 606 480 if err != nil || roundNumber >= len(pull.Submissions) { 607 481 http.Error(w, "bad round id", http.StatusBadRequest) 608 482 log.Println("failed to parse round id", err) 609 - roundSpan.End() 610 483 return 611 484 } 612 - roundSpan.End() 613 485 614 486 switch r.Method { 615 487 case http.MethodGet: 616 - _, renderSpan := s.t.TraceStart(ctx, "renderCommentFragment") 617 488 s.pages.PullNewCommentFragment(w, pages.PullNewCommentParams{ 618 489 LoggedInUser: user, 619 - RepoInfo: f.RepoInfo(ctx, s, user), 490 + RepoInfo: f.RepoInfo(s, user), 620 491 Pull: pull, 621 492 RoundNumber: roundNumber, 622 493 }) 623 - renderSpan.End() 624 494 return 625 495 case http.MethodPost: 626 - postCtx, postSpan := s.t.TraceStart(ctx, "CreateComment") 627 - defer postSpan.End() 628 - 629 - _, validateSpan := s.t.TraceStart(postCtx, "validateComment") 630 496 body := r.FormValue("body") 631 497 if body == "" { 632 498 s.pages.Notice(w, "pull", "Comment body is required") 633 - validateSpan.End() 634 499 return 635 500 } 636 - validateSpan.End() 637 501 638 502 // Start a transaction 639 - _, txSpan := s.t.TraceStart(postCtx, "startTransaction") 640 - tx, err := s.db.BeginTx(postCtx, nil) 503 + tx, err := s.db.BeginTx(r.Context(), nil) 641 504 if err != nil { 642 505 log.Println("failed to start transaction", err) 643 506 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 644 - txSpan.End() 645 507 return 646 508 } 647 509 defer tx.Rollback() 648 - txSpan.End() 649 510 650 511 createdAt := time.Now().Format(time.RFC3339) 651 512 ownerDid := user.Did 652 513 653 - _, pullAtSpan := s.t.TraceStart(postCtx, "getPullAt") 654 - pullAt, err := db.GetPullAt(postCtx, s.db, f.RepoAt, pull.PullId) 514 + pullAt, err := db.GetPullAt(s.db, f.RepoAt, pull.PullId) 655 515 if err != nil { 656 516 log.Println("failed to get pull at", err) 657 517 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 658 - pullAtSpan.End() 659 518 return 660 519 } 661 - pullAtSpan.End() 662 520 663 - _, atProtoSpan := s.t.TraceStart(postCtx, "createAtProtoRecord") 664 521 atUri := f.RepoAt.String() 665 - client, _ := s.auth.AuthorizedClient(r.WithContext(postCtx)) 666 - atResp, err := comatproto.RepoPutRecord(postCtx, client, &comatproto.RepoPutRecord_Input{ 522 + client, _ := s.auth.AuthorizedClient(r) 523 + atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 667 524 Collection: tangled.RepoPullCommentNSID, 668 525 Repo: user.Did, 669 526 Rkey: appview.TID(), ··· 680 537 if err != nil { 681 538 log.Println("failed to create pull comment", err) 682 539 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 683 - atProtoSpan.End() 684 540 return 685 541 } 686 - atProtoSpan.End() 687 542 688 543 // Create the pull comment in the database with the commentAt field 689 - _, dbSpan := s.t.TraceStart(postCtx, "createDbComment") 690 - commentId, err := db.NewPullComment(postCtx, tx, &db.PullComment{ 544 + commentId, err := db.NewPullComment(tx, &db.PullComment{ 691 545 OwnerDid: user.Did, 692 546 RepoAt: f.RepoAt.String(), 693 547 PullId: pull.PullId, ··· 698 552 if err != nil { 699 553 log.Println("failed to create pull comment", err) 700 554 s.pages.Notice(w, "pull-comment", "Failed to create comment.") 701 - dbSpan.End() 702 555 return 703 556 } 704 - dbSpan.End() 705 557 558 + // Commit the transaction 706 559 if err = tx.Commit(); err != nil { 707 560 log.Println("failed to commit transaction", err) 708 561 s.pages.Notice(w, "pull-comment", "Failed to create comment.") ··· 715 568 } 716 569 717 570 func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { 718 - ctx, span := s.t.TraceStart(r.Context(), "NewPull") 719 - defer span.End() 720 - 721 - user := s.auth.GetUser(r.WithContext(ctx)) 722 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 571 + user := s.auth.GetUser(r) 572 + f, err := s.fullyResolvedRepo(r) 723 573 if err != nil { 724 574 log.Println("failed to get repo and knot", err) 725 - span.RecordError(err) 726 575 return 727 576 } 728 577 729 578 switch r.Method { 730 579 case http.MethodGet: 731 - span.SetAttributes(attribute.String("method", "GET")) 732 - 733 580 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 734 581 if err != nil { 735 582 log.Printf("failed to create unsigned client for %s", f.Knot) 736 - span.RecordError(err) 737 583 s.pages.Error503(w) 738 584 return 739 585 } ··· 741 587 resp, err := us.Branches(f.OwnerDid(), f.RepoName) 742 588 if err != nil { 743 589 log.Println("failed to reach knotserver", err) 744 - span.RecordError(err) 745 590 return 746 591 } 747 592 748 593 body, err := io.ReadAll(resp.Body) 749 594 if err != nil { 750 595 log.Printf("Error reading response body: %v", err) 751 - span.RecordError(err) 752 596 return 753 597 } 754 598 ··· 756 600 err = json.Unmarshal(body, &result) 757 601 if err != nil { 758 602 log.Println("failed to parse response:", err) 759 - span.RecordError(err) 760 603 return 761 604 } 762 605 763 606 s.pages.RepoNewPull(w, pages.RepoNewPullParams{ 764 607 LoggedInUser: user, 765 - RepoInfo: f.RepoInfo(ctx, s, user), 608 + RepoInfo: f.RepoInfo(s, user), 766 609 Branches: result.Branches, 767 610 }) 768 611 case http.MethodPost: 769 - span.SetAttributes(attribute.String("method", "POST")) 770 - 771 612 title := r.FormValue("title") 772 613 body := r.FormValue("body") 773 614 targetBranch := r.FormValue("targetBranch") 774 615 fromFork := r.FormValue("fork") 775 616 sourceBranch := r.FormValue("sourceBranch") 776 617 patch := r.FormValue("patch") 777 - 778 - span.SetAttributes( 779 - attribute.String("targetBranch", targetBranch), 780 - attribute.String("sourceBranch", sourceBranch), 781 - attribute.Bool("hasFork", fromFork != ""), 782 - attribute.Bool("hasPatch", patch != ""), 783 - ) 784 618 785 619 if targetBranch == "" { 786 620 s.pages.Notice(w, "pull", "Target branch is required.") 787 - span.SetAttributes(attribute.String("error", "missing_target_branch")) 788 621 return 789 622 } 790 623 791 624 // Determine PR type based on input parameters 792 - isPushAllowed := f.RepoInfo(ctx, s, user).Roles.IsPushAllowed() 625 + isPushAllowed := f.RepoInfo(s, user).Roles.IsPushAllowed() 793 626 isBranchBased := isPushAllowed && sourceBranch != "" && fromFork == "" 794 627 isForkBased := fromFork != "" && sourceBranch != "" 795 628 isPatchBased := patch != "" && !isBranchBased && !isForkBased 796 629 797 - span.SetAttributes( 798 - attribute.Bool("isPushAllowed", isPushAllowed), 799 - attribute.Bool("isBranchBased", isBranchBased), 800 - attribute.Bool("isForkBased", isForkBased), 801 - attribute.Bool("isPatchBased", isPatchBased), 802 - ) 803 - 804 630 if isPatchBased && !patchutil.IsFormatPatch(patch) { 805 631 if title == "" { 806 632 s.pages.Notice(w, "pull", "Title is required for git-diff patches.") 807 - span.SetAttributes(attribute.String("error", "missing_title_for_git_diff")) 808 633 return 809 634 } 810 635 } ··· 812 637 // Validate we have at least one valid PR creation method 813 638 if !isBranchBased && !isPatchBased && !isForkBased { 814 639 s.pages.Notice(w, "pull", "Neither source branch nor patch supplied.") 815 - span.SetAttributes(attribute.String("error", "no_valid_pr_method")) 816 640 return 817 641 } 818 642 819 643 // Can't mix branch-based and patch-based approaches 820 644 if isBranchBased && patch != "" { 821 645 s.pages.Notice(w, "pull", "Cannot select both patch and source branch.") 822 - span.SetAttributes(attribute.String("error", "mixed_pr_methods")) 823 646 return 824 647 } 825 648 826 649 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 827 650 if err != nil { 828 651 log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) 829 - span.RecordError(err) 830 652 s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 831 653 return 832 654 } ··· 834 656 caps, err := us.Capabilities() 835 657 if err != nil { 836 658 log.Println("error fetching knot caps", f.Knot, err) 837 - span.RecordError(err) 838 659 s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") 839 660 return 840 661 } 841 662 842 - span.SetAttributes( 843 - attribute.Bool("caps.pullRequests.formatPatch", caps.PullRequests.FormatPatch), 844 - attribute.Bool("caps.pullRequests.branchSubmissions", caps.PullRequests.BranchSubmissions), 845 - attribute.Bool("caps.pullRequests.forkSubmissions", caps.PullRequests.ForkSubmissions), 846 - attribute.Bool("caps.pullRequests.patchSubmissions", caps.PullRequests.PatchSubmissions), 847 - ) 848 - 849 663 if !caps.PullRequests.FormatPatch { 850 664 s.pages.Notice(w, "pull", "This knot doesn't support format-patch. Unfortunately, there is no fallback for now.") 851 - span.SetAttributes(attribute.String("error", "formatpatch_not_supported")) 852 665 return 853 666 } 854 667 ··· 856 669 if isBranchBased { 857 670 if !caps.PullRequests.BranchSubmissions { 858 671 s.pages.Notice(w, "pull", "This knot doesn't support branch-based pull requests. Try another way?") 859 - span.SetAttributes(attribute.String("error", "branch_submissions_not_supported")) 860 672 return 861 673 } 862 - s.handleBranchBasedPull(w, r.WithContext(ctx), f, user, title, body, targetBranch, sourceBranch) 674 + s.handleBranchBasedPull(w, r, f, user, title, body, targetBranch, sourceBranch) 863 675 } else if isForkBased { 864 676 if !caps.PullRequests.ForkSubmissions { 865 677 s.pages.Notice(w, "pull", "This knot doesn't support fork-based pull requests. Try another way?") 866 - span.SetAttributes(attribute.String("error", "fork_submissions_not_supported")) 867 678 return 868 679 } 869 - s.handleForkBasedPull(w, r.WithContext(ctx), f, user, fromFork, title, body, targetBranch, sourceBranch) 680 + s.handleForkBasedPull(w, r, f, user, fromFork, title, body, targetBranch, sourceBranch) 870 681 } else if isPatchBased { 871 682 if !caps.PullRequests.PatchSubmissions { 872 683 s.pages.Notice(w, "pull", "This knot doesn't support patch-based pull requests. Send your patch over email.") 873 - span.SetAttributes(attribute.String("error", "patch_submissions_not_supported")) 874 684 return 875 685 } 876 - s.handlePatchBasedPull(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch) 686 + s.handlePatchBasedPull(w, r, f, user, title, body, targetBranch, patch) 877 687 } 878 688 return 879 689 } 880 690 } 881 691 882 692 func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) { 883 - ctx, span := s.t.TraceStart(r.Context(), "handleBranchBasedPull") 884 - defer span.End() 885 - 886 - span.SetAttributes( 887 - attribute.String("targetBranch", targetBranch), 888 - attribute.String("sourceBranch", sourceBranch), 889 - ) 890 - 891 693 pullSource := &db.PullSource{ 892 694 Branch: sourceBranch, 893 695 } ··· 899 701 ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 900 702 if err != nil { 901 703 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 902 - span.RecordError(err) 903 - span.SetAttributes(attribute.String("error", "client_creation_failed")) 904 704 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 905 705 return 906 706 } ··· 908 708 comparison, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch) 909 709 if err != nil { 910 710 log.Println("failed to compare", err) 911 - span.RecordError(err) 912 - span.SetAttributes(attribute.String("error", "comparison_failed")) 913 711 s.pages.Notice(w, "pull", err.Error()) 914 712 return 915 713 } ··· 917 715 sourceRev := comparison.Rev2 918 716 patch := comparison.Patch 919 717 920 - span.SetAttributes(attribute.String("sourceRev", sourceRev)) 921 - 922 718 if !patchutil.IsPatchValid(patch) { 923 - span.SetAttributes(attribute.String("error", "invalid_patch_format")) 924 719 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 925 720 return 926 721 } 927 722 928 - s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource) 723 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource) 929 724 } 930 725 931 726 func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) { 932 - ctx, span := s.t.TraceStart(r.Context(), "handlePatchBasedPull") 933 - defer span.End() 934 - 935 - span.SetAttributes(attribute.String("targetBranch", targetBranch)) 936 - 937 727 if !patchutil.IsPatchValid(patch) { 938 - span.SetAttributes(attribute.String("error", "invalid_patch_format")) 939 728 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 940 729 return 941 730 } 942 731 943 - s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, "", nil, nil) 732 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil) 944 733 } 945 734 946 735 func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { 947 - ctx, span := s.t.TraceStart(r.Context(), "handleForkBasedPull") 948 - defer span.End() 949 - 950 - span.SetAttributes( 951 - attribute.String("forkRepo", forkRepo), 952 - attribute.String("targetBranch", targetBranch), 953 - attribute.String("sourceBranch", sourceBranch), 954 - ) 955 - 956 - fork, err := db.GetForkByDid(ctx, s.db, user.Did, forkRepo) 736 + fork, err := db.GetForkByDid(s.db, user.Did, forkRepo) 957 737 if errors.Is(err, sql.ErrNoRows) { 958 - span.SetAttributes(attribute.String("error", "fork_not_found")) 959 738 s.pages.Notice(w, "pull", "No such fork.") 960 739 return 961 740 } else if err != nil { 962 741 log.Println("failed to fetch fork:", err) 963 - span.RecordError(err) 964 - span.SetAttributes(attribute.String("error", "fork_fetch_failed")) 965 742 s.pages.Notice(w, "pull", "Failed to fetch fork.") 966 743 return 967 744 } ··· 969 746 secret, err := db.GetRegistrationKey(s.db, fork.Knot) 970 747 if err != nil { 971 748 log.Println("failed to fetch registration key:", err) 972 - span.RecordError(err) 973 - span.SetAttributes(attribute.String("error", "registration_key_fetch_failed")) 974 749 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 975 750 return 976 751 } ··· 978 753 sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev) 979 754 if err != nil { 980 755 log.Println("failed to create signed client:", err) 981 - span.RecordError(err) 982 - span.SetAttributes(attribute.String("error", "signed_client_creation_failed")) 983 756 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 984 757 return 985 758 } ··· 987 760 us, err := NewUnsignedClient(fork.Knot, s.config.Dev) 988 761 if err != nil { 989 762 log.Println("failed to create unsigned client:", err) 990 - span.RecordError(err) 991 - span.SetAttributes(attribute.String("error", "unsigned_client_creation_failed")) 992 763 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 993 764 return 994 765 } ··· 996 767 resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch) 997 768 if err != nil { 998 769 log.Println("failed to create hidden ref:", err, resp.StatusCode) 999 - span.RecordError(err) 1000 - span.SetAttributes(attribute.String("error", "hidden_ref_creation_failed")) 1001 770 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1002 771 return 1003 772 } 1004 773 1005 774 switch resp.StatusCode { 1006 775 case 404: 1007 - span.SetAttributes(attribute.String("error", "not_found_status")) 1008 776 case 400: 1009 - span.SetAttributes(attribute.String("error", "bad_request_status")) 1010 777 s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.") 1011 778 return 1012 779 } 1013 780 1014 781 hiddenRef := fmt.Sprintf("hidden/%s/%s", sourceBranch, targetBranch) 1015 - span.SetAttributes(attribute.String("hiddenRef", hiddenRef)) 1016 - 1017 782 // We're now comparing the sourceBranch (on the fork) against the hiddenRef which is tracking 1018 783 // the targetBranch on the target repository. This code is a bit confusing, but here's an example: 1019 784 // hiddenRef: hidden/feature-1/main (on repo-fork) ··· 1022 787 comparison, err := us.Compare(user.Did, fork.Name, hiddenRef, sourceBranch) 1023 788 if err != nil { 1024 789 log.Println("failed to compare across branches", err) 1025 - span.RecordError(err) 1026 - span.SetAttributes(attribute.String("error", "branch_comparison_failed")) 1027 790 s.pages.Notice(w, "pull", err.Error()) 1028 791 return 1029 792 } 1030 793 1031 794 sourceRev := comparison.Rev2 1032 795 patch := comparison.Patch 1033 - span.SetAttributes(attribute.String("sourceRev", sourceRev)) 1034 796 1035 797 if !patchutil.IsPatchValid(patch) { 1036 - span.SetAttributes(attribute.String("error", "invalid_patch_format")) 1037 798 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 1038 799 return 1039 800 } ··· 1041 802 forkAtUri, err := syntax.ParseATURI(fork.AtUri) 1042 803 if err != nil { 1043 804 log.Println("failed to parse fork AT URI", err) 1044 - span.RecordError(err) 1045 - span.SetAttributes(attribute.String("error", "fork_aturi_parse_failed")) 1046 805 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1047 806 return 1048 807 } 1049 808 1050 - s.createPullRequest(w, r.WithContext(ctx), f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{ 809 + s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{ 1051 810 Branch: sourceBranch, 1052 811 RepoAt: &forkAtUri, 1053 812 }, &tangled.RepoPull_Source{Branch: sourceBranch, Repo: &fork.AtUri}) ··· 1064 823 pullSource *db.PullSource, 1065 824 recordPullSource *tangled.RepoPull_Source, 1066 825 ) { 1067 - ctx, span := s.t.TraceStart(r.Context(), "createPullRequest") 1068 - defer span.End() 1069 - 1070 - span.SetAttributes( 1071 - attribute.String("targetBranch", targetBranch), 1072 - attribute.String("sourceRev", sourceRev), 1073 - attribute.Bool("hasPullSource", pullSource != nil), 1074 - ) 1075 - 1076 - tx, err := s.db.BeginTx(ctx, nil) 826 + tx, err := s.db.BeginTx(r.Context(), nil) 1077 827 if err != nil { 1078 828 log.Println("failed to start tx") 1079 - span.RecordError(err) 1080 - span.SetAttributes(attribute.String("error", "transaction_start_failed")) 1081 829 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1082 830 return 1083 831 } ··· 1088 836 if title == "" { 1089 837 formatPatches, err := patchutil.ExtractPatches(patch) 1090 838 if err != nil { 1091 - span.RecordError(err) 1092 - span.SetAttributes(attribute.String("error", "extract_patches_failed")) 1093 839 s.pages.Notice(w, "pull", fmt.Sprintf("Failed to extract patches: %v", err)) 1094 840 return 1095 841 } 1096 842 if len(formatPatches) == 0 { 1097 - span.SetAttributes(attribute.String("error", "no_patches_found")) 1098 843 s.pages.Notice(w, "pull", "No patches found in the supplied format-patch.") 1099 844 return 1100 845 } 1101 846 1102 847 title = formatPatches[0].Title 1103 848 body = formatPatches[0].Body 1104 - span.SetAttributes( 1105 - attribute.Bool("title_extracted", true), 1106 - attribute.Bool("body_extracted", formatPatches[0].Body != ""), 1107 - ) 1108 849 } 1109 850 1110 851 rkey := appview.TID() ··· 1112 853 Patch: patch, 1113 854 SourceRev: sourceRev, 1114 855 } 1115 - err = db.NewPull(ctx, tx, &db.Pull{ 856 + err = db.NewPull(tx, &db.Pull{ 1116 857 Title: title, 1117 858 Body: body, 1118 859 TargetBranch: targetBranch, ··· 1126 867 }) 1127 868 if err != nil { 1128 869 log.Println("failed to create pull request", err) 1129 - span.RecordError(err) 1130 - span.SetAttributes(attribute.String("error", "db_create_pull_failed")) 1131 870 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1132 871 return 1133 872 } 1134 - 1135 - client, _ := s.auth.AuthorizedClient(r.WithContext(ctx)) 873 + client, _ := s.auth.AuthorizedClient(r) 1136 874 pullId, err := db.NextPullId(s.db, f.RepoAt) 1137 875 if err != nil { 1138 876 log.Println("failed to get pull id", err) 1139 - span.RecordError(err) 1140 - span.SetAttributes(attribute.String("error", "get_pull_id_failed")) 1141 877 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1142 878 return 1143 879 } 1144 - span.SetAttributes(attribute.Int("pullId", pullId)) 1145 880 1146 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 881 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1147 882 Collection: tangled.RepoPullNSID, 1148 883 Repo: user.Did, 1149 884 Rkey: rkey, ··· 1161 896 1162 897 if err != nil { 1163 898 log.Println("failed to create pull request", err) 1164 - span.RecordError(err) 1165 - span.SetAttributes(attribute.String("error", "atproto_create_record_failed")) 1166 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1167 - return 1168 - } 1169 - 1170 - if err = tx.Commit(); err != nil { 1171 - log.Println("failed to commit transaction", err) 1172 - span.RecordError(err) 1173 - span.SetAttributes(attribute.String("error", "transaction_commit_failed")) 1174 899 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 1175 900 return 1176 901 } ··· 1179 904 } 1180 905 1181 906 func (s *State) ValidatePatch(w http.ResponseWriter, r *http.Request) { 1182 - ctx, span := s.t.TraceStart(r.Context(), "ValidatePatch") 1183 - defer span.End() 1184 - 1185 - _, err := s.fullyResolvedRepo(r.WithContext(ctx)) 907 + _, err := s.fullyResolvedRepo(r) 1186 908 if err != nil { 1187 909 log.Println("failed to get repo and knot", err) 1188 - span.RecordError(err) 1189 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 1190 910 return 1191 911 } 1192 912 1193 913 patch := r.FormValue("patch") 1194 - span.SetAttributes(attribute.Bool("hasPatch", patch != "")) 1195 - 1196 914 if patch == "" { 1197 - span.SetAttributes(attribute.String("error", "empty_patch")) 1198 915 s.pages.Notice(w, "patch-error", "Patch is required.") 1199 916 return 1200 917 } 1201 918 1202 - if !patchutil.IsPatchValid(patch) { 1203 - span.SetAttributes(attribute.String("error", "invalid_patch_format")) 919 + if patch == "" || !patchutil.IsPatchValid(patch) { 1204 920 s.pages.Notice(w, "patch-error", "Invalid patch format. Please provide a valid git diff or format-patch.") 1205 921 return 1206 922 } 1207 923 1208 - isFormatPatch := patchutil.IsFormatPatch(patch) 1209 - span.SetAttributes(attribute.Bool("isFormatPatch", isFormatPatch)) 1210 - 1211 - if isFormatPatch { 924 + if patchutil.IsFormatPatch(patch) { 1212 925 s.pages.Notice(w, "patch-preview", "git-format-patch detected. Title and description are optional; if left out, they will be extracted from the first commit.") 1213 926 } else { 1214 927 s.pages.Notice(w, "patch-preview", "Regular git-diff detected. Please provide a title and description.") ··· 1216 929 } 1217 930 1218 931 func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) { 1219 - ctx, span := s.t.TraceStart(r.Context(), "PatchUploadFragment") 1220 - defer span.End() 1221 - 1222 - user := s.auth.GetUser(r.WithContext(ctx)) 1223 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 932 + user := s.auth.GetUser(r) 933 + f, err := s.fullyResolvedRepo(r) 1224 934 if err != nil { 1225 935 log.Println("failed to get repo and knot", err) 1226 - span.RecordError(err) 1227 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 1228 936 return 1229 937 } 1230 938 1231 939 s.pages.PullPatchUploadFragment(w, pages.PullPatchUploadParams{ 1232 - RepoInfo: f.RepoInfo(ctx, s, user), 940 + RepoInfo: f.RepoInfo(s, user), 1233 941 }) 1234 942 } 1235 943 1236 944 func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) { 1237 - ctx, span := s.t.TraceStart(r.Context(), "CompareBranchesFragment") 1238 - defer span.End() 1239 - 1240 - user := s.auth.GetUser(r.WithContext(ctx)) 1241 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 945 + user := s.auth.GetUser(r) 946 + f, err := s.fullyResolvedRepo(r) 1242 947 if err != nil { 1243 948 log.Println("failed to get repo and knot", err) 1244 - span.RecordError(err) 1245 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 1246 949 return 1247 950 } 1248 951 1249 952 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 1250 953 if err != nil { 1251 954 log.Printf("failed to create unsigned client for %s", f.Knot) 1252 - span.RecordError(err) 1253 - span.SetAttributes(attribute.String("error", "client_creation_failed")) 1254 955 s.pages.Error503(w) 1255 956 return 1256 957 } ··· 1258 959 resp, err := us.Branches(f.OwnerDid(), f.RepoName) 1259 960 if err != nil { 1260 961 log.Println("failed to reach knotserver", err) 1261 - span.RecordError(err) 1262 - span.SetAttributes(attribute.String("error", "knotserver_connection_failed")) 1263 962 return 1264 963 } 1265 964 1266 965 body, err := io.ReadAll(resp.Body) 1267 966 if err != nil { 1268 967 log.Printf("Error reading response body: %v", err) 1269 - span.RecordError(err) 1270 - span.SetAttributes(attribute.String("error", "response_read_failed")) 1271 968 return 1272 969 } 1273 - defer resp.Body.Close() 1274 970 1275 971 var result types.RepoBranchesResponse 1276 972 err = json.Unmarshal(body, &result) 1277 973 if err != nil { 1278 974 log.Println("failed to parse response:", err) 1279 - span.RecordError(err) 1280 - span.SetAttributes(attribute.String("error", "response_parse_failed")) 1281 975 return 1282 976 } 1283 - span.SetAttributes(attribute.Int("branches.count", len(result.Branches))) 1284 977 1285 978 s.pages.PullCompareBranchesFragment(w, pages.PullCompareBranchesParams{ 1286 - RepoInfo: f.RepoInfo(ctx, s, user), 979 + RepoInfo: f.RepoInfo(s, user), 1287 980 Branches: result.Branches, 1288 981 }) 1289 982 } 1290 983 1291 984 func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { 1292 - ctx, span := s.t.TraceStart(r.Context(), "CompareForksFragment") 1293 - defer span.End() 1294 - 1295 - user := s.auth.GetUser(r.WithContext(ctx)) 1296 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 985 + user := s.auth.GetUser(r) 986 + f, err := s.fullyResolvedRepo(r) 1297 987 if err != nil { 1298 988 log.Println("failed to get repo and knot", err) 1299 - span.RecordError(err) 1300 989 return 1301 990 } 1302 991 1303 - forks, err := db.GetForksByDid(ctx, s.db, user.Did) 992 + forks, err := db.GetForksByDid(s.db, user.Did) 1304 993 if err != nil { 1305 994 log.Println("failed to get forks", err) 1306 - span.RecordError(err) 1307 995 return 1308 996 } 1309 997 1310 998 s.pages.PullCompareForkFragment(w, pages.PullCompareForkParams{ 1311 - RepoInfo: f.RepoInfo(ctx, s, user), 999 + RepoInfo: f.RepoInfo(s, user), 1312 1000 Forks: forks, 1313 1001 }) 1314 1002 } 1315 1003 1316 1004 func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) { 1317 - ctx, span := s.t.TraceStart(r.Context(), "CompareForksBranchesFragment") 1318 - defer span.End() 1005 + user := s.auth.GetUser(r) 1319 1006 1320 - user := s.auth.GetUser(r.WithContext(ctx)) 1321 - 1322 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1007 + f, err := s.fullyResolvedRepo(r) 1323 1008 if err != nil { 1324 1009 log.Println("failed to get repo and knot", err) 1325 - span.RecordError(err) 1326 1010 return 1327 1011 } 1328 1012 1329 1013 forkVal := r.URL.Query().Get("fork") 1330 - span.SetAttributes(attribute.String("fork", forkVal)) 1331 1014 1332 1015 // fork repo 1333 - repo, err := db.GetRepo(ctx, s.db, user.Did, forkVal) 1016 + repo, err := db.GetRepo(s.db, user.Did, forkVal) 1334 1017 if err != nil { 1335 1018 log.Println("failed to get repo", user.Did, forkVal) 1336 - span.RecordError(err) 1337 1019 return 1338 1020 } 1339 1021 1340 1022 sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev) 1341 1023 if err != nil { 1342 1024 log.Printf("failed to create unsigned client for %s", repo.Knot) 1343 - span.RecordError(err) 1344 1025 s.pages.Error503(w) 1345 1026 return 1346 1027 } ··· 1348 1029 sourceResp, err := sourceBranchesClient.Branches(user.Did, repo.Name) 1349 1030 if err != nil { 1350 1031 log.Println("failed to reach knotserver for source branches", err) 1351 - span.RecordError(err) 1352 1032 return 1353 1033 } 1354 1034 1355 1035 sourceBody, err := io.ReadAll(sourceResp.Body) 1356 1036 if err != nil { 1357 1037 log.Println("failed to read source response body", err) 1358 - span.RecordError(err) 1359 1038 return 1360 1039 } 1361 1040 defer sourceResp.Body.Close() ··· 1364 1043 err = json.Unmarshal(sourceBody, &sourceResult) 1365 1044 if err != nil { 1366 1045 log.Println("failed to parse source branches response:", err) 1367 - span.RecordError(err) 1368 1046 return 1369 1047 } 1370 1048 1371 1049 targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 1372 1050 if err != nil { 1373 1051 log.Printf("failed to create unsigned client for target knot %s", f.Knot) 1374 - span.RecordError(err) 1375 1052 s.pages.Error503(w) 1376 1053 return 1377 1054 } ··· 1379 1056 targetResp, err := targetBranchesClient.Branches(f.OwnerDid(), f.RepoName) 1380 1057 if err != nil { 1381 1058 log.Println("failed to reach knotserver for target branches", err) 1382 - span.RecordError(err) 1383 1059 return 1384 1060 } 1385 1061 1386 1062 targetBody, err := io.ReadAll(targetResp.Body) 1387 1063 if err != nil { 1388 1064 log.Println("failed to read target response body", err) 1389 - span.RecordError(err) 1390 1065 return 1391 1066 } 1392 1067 defer targetResp.Body.Close() ··· 1395 1070 err = json.Unmarshal(targetBody, &targetResult) 1396 1071 if err != nil { 1397 1072 log.Println("failed to parse target branches response:", err) 1398 - span.RecordError(err) 1399 1073 return 1400 1074 } 1401 1075 1402 1076 s.pages.PullCompareForkBranchesFragment(w, pages.PullCompareForkBranchesParams{ 1403 - RepoInfo: f.RepoInfo(ctx, s, user), 1077 + RepoInfo: f.RepoInfo(s, user), 1404 1078 SourceBranches: sourceResult.Branches, 1405 1079 TargetBranches: targetResult.Branches, 1406 1080 }) 1407 1081 } 1408 1082 1409 1083 func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) { 1410 - ctx, span := s.t.TraceStart(r.Context(), "ResubmitPull") 1411 - defer span.End() 1412 - 1413 - user := s.auth.GetUser(r.WithContext(ctx)) 1414 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1084 + user := s.auth.GetUser(r) 1085 + f, err := s.fullyResolvedRepo(r) 1415 1086 if err != nil { 1416 1087 log.Println("failed to get repo and knot", err) 1417 - span.RecordError(err) 1418 1088 return 1419 1089 } 1420 1090 1421 - pull, ok := ctx.Value("pull").(*db.Pull) 1091 + pull, ok := r.Context().Value("pull").(*db.Pull) 1422 1092 if !ok { 1423 1093 log.Println("failed to get pull") 1424 - span.RecordError(errors.New("failed to get pull from context")) 1425 1094 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 1426 1095 return 1427 1096 } 1428 1097 1429 - span.SetAttributes( 1430 - attribute.Int("pull.id", pull.PullId), 1431 - attribute.String("pull.owner", pull.OwnerDid), 1432 - attribute.String("method", r.Method), 1433 - ) 1434 - 1435 1098 switch r.Method { 1436 1099 case http.MethodGet: 1437 1100 s.pages.PullResubmitFragment(w, pages.PullResubmitParams{ 1438 - RepoInfo: f.RepoInfo(ctx, s, user), 1101 + RepoInfo: f.RepoInfo(s, user), 1439 1102 Pull: pull, 1440 1103 }) 1441 1104 return 1442 1105 case http.MethodPost: 1443 1106 if pull.IsPatchBased() { 1444 - span.SetAttributes(attribute.String("pull.type", "patch_based")) 1445 - s.resubmitPatch(w, r.WithContext(ctx)) 1107 + s.resubmitPatch(w, r) 1446 1108 return 1447 1109 } else if pull.IsBranchBased() { 1448 - span.SetAttributes(attribute.String("pull.type", "branch_based")) 1449 - s.resubmitBranch(w, r.WithContext(ctx)) 1110 + s.resubmitBranch(w, r) 1450 1111 return 1451 1112 } else if pull.IsForkBased() { 1452 - span.SetAttributes(attribute.String("pull.type", "fork_based")) 1453 - s.resubmitFork(w, r.WithContext(ctx)) 1113 + s.resubmitFork(w, r) 1454 1114 return 1455 1115 } 1456 - span.SetAttributes(attribute.String("pull.type", "unknown")) 1457 1116 } 1458 1117 } 1459 1118 1460 1119 func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { 1461 - ctx, span := s.t.TraceStart(r.Context(), "resubmitPatch") 1462 - defer span.End() 1463 - 1464 - user := s.auth.GetUser(r.WithContext(ctx)) 1120 + user := s.auth.GetUser(r) 1465 1121 1466 - pull, ok := ctx.Value("pull").(*db.Pull) 1122 + pull, ok := r.Context().Value("pull").(*db.Pull) 1467 1123 if !ok { 1468 1124 log.Println("failed to get pull") 1469 - span.RecordError(errors.New("failed to get pull from context")) 1470 1125 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 1471 1126 return 1472 1127 } 1473 1128 1474 - span.SetAttributes( 1475 - attribute.Int("pull.id", pull.PullId), 1476 - attribute.String("pull.owner", pull.OwnerDid), 1477 - ) 1478 - 1479 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1129 + f, err := s.fullyResolvedRepo(r) 1480 1130 if err != nil { 1481 1131 log.Println("failed to get repo and knot", err) 1482 - span.RecordError(err) 1483 1132 return 1484 1133 } 1485 1134 1486 1135 if user.Did != pull.OwnerDid { 1487 1136 log.Println("unauthorized user") 1488 - span.SetAttributes(attribute.String("error", "unauthorized_user")) 1489 1137 w.WriteHeader(http.StatusUnauthorized) 1490 1138 return 1491 1139 } 1492 1140 1493 1141 patch := r.FormValue("patch") 1494 - span.SetAttributes(attribute.Bool("has_patch", patch != "")) 1495 1142 1496 1143 if err = validateResubmittedPatch(pull, patch); err != nil { 1497 - span.SetAttributes(attribute.String("error", "invalid_patch")) 1498 1144 s.pages.Notice(w, "resubmit-error", err.Error()) 1499 1145 return 1500 1146 } 1501 1147 1502 - tx, err := s.db.BeginTx(ctx, nil) 1148 + tx, err := s.db.BeginTx(r.Context(), nil) 1503 1149 if err != nil { 1504 1150 log.Println("failed to start tx") 1505 - span.RecordError(err) 1506 1151 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1507 1152 return 1508 1153 } ··· 1511 1156 err = db.ResubmitPull(tx, pull, patch, "") 1512 1157 if err != nil { 1513 1158 log.Println("failed to resubmit pull request", err) 1514 - span.RecordError(err) 1515 1159 s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") 1516 1160 return 1517 1161 } 1518 - client, _ := s.auth.AuthorizedClient(r.WithContext(ctx)) 1162 + client, _ := s.auth.AuthorizedClient(r) 1519 1163 1520 - ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1164 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1521 1165 if err != nil { 1522 1166 // failed to get record 1523 - span.RecordError(err) 1524 - span.SetAttributes(attribute.String("error", "record_not_found")) 1525 1167 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") 1526 1168 return 1527 1169 } 1528 1170 1529 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1171 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1530 1172 Collection: tangled.RepoPullNSID, 1531 1173 Repo: user.Did, 1532 1174 Rkey: pull.Rkey, ··· 1543 1185 }) 1544 1186 if err != nil { 1545 1187 log.Println("failed to update record", err) 1546 - span.RecordError(err) 1547 1188 s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.") 1548 1189 return 1549 1190 } 1550 1191 1551 1192 if err = tx.Commit(); err != nil { 1552 1193 log.Println("failed to commit transaction", err) 1553 - span.RecordError(err) 1554 1194 s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.") 1555 1195 return 1556 1196 } ··· 1560 1200 } 1561 1201 1562 1202 func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { 1563 - ctx, span := s.t.TraceStart(r.Context(), "resubmitBranch") 1564 - defer span.End() 1203 + user := s.auth.GetUser(r) 1565 1204 1566 - user := s.auth.GetUser(r.WithContext(ctx)) 1567 - 1568 - pull, ok := ctx.Value("pull").(*db.Pull) 1205 + pull, ok := r.Context().Value("pull").(*db.Pull) 1569 1206 if !ok { 1570 1207 log.Println("failed to get pull") 1571 - span.RecordError(errors.New("failed to get pull from context")) 1572 - s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 1208 + s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.") 1573 1209 return 1574 1210 } 1575 1211 1576 - span.SetAttributes( 1577 - attribute.Int("pull.id", pull.PullId), 1578 - attribute.String("pull.owner", pull.OwnerDid), 1579 - attribute.String("pull.source_branch", pull.PullSource.Branch), 1580 - attribute.String("pull.target_branch", pull.TargetBranch), 1581 - ) 1582 - 1583 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1212 + f, err := s.fullyResolvedRepo(r) 1584 1213 if err != nil { 1585 1214 log.Println("failed to get repo and knot", err) 1586 - span.RecordError(err) 1587 1215 return 1588 1216 } 1589 1217 1590 1218 if user.Did != pull.OwnerDid { 1591 1219 log.Println("unauthorized user") 1592 - span.SetAttributes(attribute.String("error", "unauthorized_user")) 1593 1220 w.WriteHeader(http.StatusUnauthorized) 1594 1221 return 1595 1222 } 1596 1223 1597 - if !f.RepoInfo(ctx, s, user).Roles.IsPushAllowed() { 1224 + if !f.RepoInfo(s, user).Roles.IsPushAllowed() { 1598 1225 log.Println("unauthorized user") 1599 - span.SetAttributes(attribute.String("error", "push_not_allowed")) 1600 1226 w.WriteHeader(http.StatusUnauthorized) 1601 1227 return 1602 1228 } ··· 1604 1230 ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 1605 1231 if err != nil { 1606 1232 log.Printf("failed to create client for %s: %s", f.Knot, err) 1607 - span.RecordError(err) 1608 - span.SetAttributes(attribute.String("error", "client_creation_failed")) 1609 1233 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1610 1234 return 1611 1235 } ··· 1613 1237 comparison, err := ksClient.Compare(f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.PullSource.Branch) 1614 1238 if err != nil { 1615 1239 log.Printf("compare request failed: %s", err) 1616 - span.RecordError(err) 1617 - span.SetAttributes(attribute.String("error", "compare_failed")) 1618 1240 s.pages.Notice(w, "resubmit-error", err.Error()) 1619 1241 return 1620 1242 } 1621 1243 1622 1244 sourceRev := comparison.Rev2 1623 1245 patch := comparison.Patch 1624 - span.SetAttributes(attribute.String("source_rev", sourceRev)) 1625 1246 1626 1247 if err = validateResubmittedPatch(pull, patch); err != nil { 1627 - span.SetAttributes(attribute.String("error", "invalid_patch")) 1628 1248 s.pages.Notice(w, "resubmit-error", err.Error()) 1629 1249 return 1630 1250 } 1631 1251 1632 1252 if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev { 1633 - span.SetAttributes(attribute.String("error", "no_changes")) 1634 1253 s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.") 1635 1254 return 1636 1255 } 1637 1256 1638 - tx, err := s.db.BeginTx(ctx, nil) 1257 + tx, err := s.db.BeginTx(r.Context(), nil) 1639 1258 if err != nil { 1640 1259 log.Println("failed to start tx") 1641 - span.RecordError(err) 1642 1260 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1643 1261 return 1644 1262 } ··· 1647 1265 err = db.ResubmitPull(tx, pull, patch, sourceRev) 1648 1266 if err != nil { 1649 1267 log.Println("failed to create pull request", err) 1650 - span.RecordError(err) 1651 1268 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1652 1269 return 1653 1270 } 1654 - client, _ := s.auth.AuthorizedClient(r.WithContext(ctx)) 1271 + client, _ := s.auth.AuthorizedClient(r) 1655 1272 1656 - ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1273 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1657 1274 if err != nil { 1658 1275 // failed to get record 1659 - span.RecordError(err) 1660 - span.SetAttributes(attribute.String("error", "record_not_found")) 1661 1276 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") 1662 1277 return 1663 1278 } ··· 1665 1280 recordPullSource := &tangled.RepoPull_Source{ 1666 1281 Branch: pull.PullSource.Branch, 1667 1282 } 1668 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1283 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1669 1284 Collection: tangled.RepoPullNSID, 1670 1285 Repo: user.Did, 1671 1286 Rkey: pull.Rkey, ··· 1683 1298 }) 1684 1299 if err != nil { 1685 1300 log.Println("failed to update record", err) 1686 - span.RecordError(err) 1687 1301 s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.") 1688 1302 return 1689 1303 } 1690 1304 1691 1305 if err = tx.Commit(); err != nil { 1692 1306 log.Println("failed to commit transaction", err) 1693 - span.RecordError(err) 1694 1307 s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.") 1695 1308 return 1696 1309 } ··· 1700 1313 } 1701 1314 1702 1315 func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { 1703 - ctx, span := s.t.TraceStart(r.Context(), "resubmitFork") 1704 - defer span.End() 1705 - 1706 - user := s.auth.GetUser(r.WithContext(ctx)) 1316 + user := s.auth.GetUser(r) 1707 1317 1708 - pull, ok := ctx.Value("pull").(*db.Pull) 1318 + pull, ok := r.Context().Value("pull").(*db.Pull) 1709 1319 if !ok { 1710 1320 log.Println("failed to get pull") 1711 - span.RecordError(errors.New("failed to get pull from context")) 1712 - s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 1321 + s.pages.Notice(w, "resubmit-error", "Failed to edit patch. Try again later.") 1713 1322 return 1714 1323 } 1715 1324 1716 - span.SetAttributes( 1717 - attribute.Int("pull.id", pull.PullId), 1718 - attribute.String("pull.owner", pull.OwnerDid), 1719 - attribute.String("pull.source_branch", pull.PullSource.Branch), 1720 - attribute.String("pull.target_branch", pull.TargetBranch), 1721 - ) 1722 - 1723 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1325 + f, err := s.fullyResolvedRepo(r) 1724 1326 if err != nil { 1725 1327 log.Println("failed to get repo and knot", err) 1726 - span.RecordError(err) 1727 1328 return 1728 1329 } 1729 1330 1730 1331 if user.Did != pull.OwnerDid { 1731 1332 log.Println("unauthorized user") 1732 - span.SetAttributes(attribute.String("error", "unauthorized_user")) 1733 1333 w.WriteHeader(http.StatusUnauthorized) 1734 1334 return 1735 1335 } 1736 1336 1737 - forkRepo, err := db.GetRepoByAtUri(ctx, s.db, pull.PullSource.RepoAt.String()) 1337 + forkRepo, err := db.GetRepoByAtUri(s.db, pull.PullSource.RepoAt.String()) 1738 1338 if err != nil { 1739 1339 log.Println("failed to get source repo", err) 1740 - span.RecordError(err) 1741 - span.SetAttributes(attribute.String("error", "source_repo_not_found")) 1742 1340 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1743 1341 return 1744 1342 } 1745 1343 1746 - span.SetAttributes( 1747 - attribute.String("fork.knot", forkRepo.Knot), 1748 - attribute.String("fork.did", forkRepo.Did), 1749 - attribute.String("fork.name", forkRepo.Name), 1750 - ) 1751 - 1752 1344 // extract patch by performing compare 1753 1345 ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev) 1754 1346 if err != nil { 1755 1347 log.Printf("failed to create client for %s: %s", forkRepo.Knot, err) 1756 - span.RecordError(err) 1757 - span.SetAttributes(attribute.String("error", "client_creation_failed")) 1758 1348 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1759 1349 return 1760 1350 } ··· 1762 1352 secret, err := db.GetRegistrationKey(s.db, forkRepo.Knot) 1763 1353 if err != nil { 1764 1354 log.Printf("failed to get registration key for %s: %s", forkRepo.Knot, err) 1765 - span.RecordError(err) 1766 - span.SetAttributes(attribute.String("error", "reg_key_not_found")) 1767 1355 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1768 1356 return 1769 1357 } ··· 1772 1360 signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev) 1773 1361 if err != nil { 1774 1362 log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) 1775 - span.RecordError(err) 1776 - span.SetAttributes(attribute.String("error", "signed_client_creation_failed")) 1777 1363 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1778 1364 return 1779 1365 } ··· 1781 1367 resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch) 1782 1368 if err != nil || resp.StatusCode != http.StatusNoContent { 1783 1369 log.Printf("failed to update tracking branch: %s", err) 1784 - span.RecordError(err) 1785 - span.SetAttributes(attribute.String("error", "hidden_ref_update_failed")) 1786 1370 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1787 1371 return 1788 1372 } 1789 1373 1790 1374 hiddenRef := fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch) 1791 - span.SetAttributes(attribute.String("hidden_ref", hiddenRef)) 1792 - 1793 1375 comparison, err := ksClient.Compare(forkRepo.Did, forkRepo.Name, hiddenRef, pull.PullSource.Branch) 1794 1376 if err != nil { 1795 1377 log.Printf("failed to compare branches: %s", err) 1796 - span.RecordError(err) 1797 - span.SetAttributes(attribute.String("error", "compare_failed")) 1798 1378 s.pages.Notice(w, "resubmit-error", err.Error()) 1799 1379 return 1800 1380 } 1801 1381 1802 1382 sourceRev := comparison.Rev2 1803 1383 patch := comparison.Patch 1804 - span.SetAttributes(attribute.String("source_rev", sourceRev)) 1805 1384 1806 1385 if err = validateResubmittedPatch(pull, patch); err != nil { 1807 - span.SetAttributes(attribute.String("error", "invalid_patch")) 1808 1386 s.pages.Notice(w, "resubmit-error", err.Error()) 1809 1387 return 1810 1388 } 1811 1389 1812 1390 if sourceRev == pull.Submissions[pull.LastRoundNumber()].SourceRev { 1813 - span.SetAttributes(attribute.String("error", "no_changes")) 1814 1391 s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.") 1815 1392 return 1816 1393 } 1817 1394 1818 - tx, err := s.db.BeginTx(ctx, nil) 1395 + tx, err := s.db.BeginTx(r.Context(), nil) 1819 1396 if err != nil { 1820 1397 log.Println("failed to start tx") 1821 - span.RecordError(err) 1822 1398 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1823 1399 return 1824 1400 } ··· 1827 1403 err = db.ResubmitPull(tx, pull, patch, sourceRev) 1828 1404 if err != nil { 1829 1405 log.Println("failed to create pull request", err) 1830 - span.RecordError(err) 1831 1406 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1832 1407 return 1833 1408 } 1834 - client, _ := s.auth.AuthorizedClient(r.WithContext(ctx)) 1409 + client, _ := s.auth.AuthorizedClient(r) 1835 1410 1836 - ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1411 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1837 1412 if err != nil { 1838 1413 // failed to get record 1839 - span.RecordError(err) 1840 - span.SetAttributes(attribute.String("error", "record_not_found")) 1841 1414 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") 1842 1415 return 1843 1416 } ··· 1847 1420 Branch: pull.PullSource.Branch, 1848 1421 Repo: &repoAt, 1849 1422 } 1850 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1423 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1851 1424 Collection: tangled.RepoPullNSID, 1852 1425 Repo: user.Did, 1853 1426 Rkey: pull.Rkey, ··· 1865 1438 }) 1866 1439 if err != nil { 1867 1440 log.Println("failed to update record", err) 1868 - span.RecordError(err) 1869 1441 s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.") 1870 1442 return 1871 1443 } 1872 1444 1873 1445 if err = tx.Commit(); err != nil { 1874 1446 log.Println("failed to commit transaction", err) 1875 - span.RecordError(err) 1876 1447 s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull.") 1877 1448 return 1878 1449 } ··· 1899 1470 } 1900 1471 1901 1472 func (s *State) MergePull(w http.ResponseWriter, r *http.Request) { 1902 - ctx, span := s.t.TraceStart(r.Context(), "MergePull") 1903 - defer span.End() 1904 - 1905 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1473 + f, err := s.fullyResolvedRepo(r) 1906 1474 if err != nil { 1907 1475 log.Println("failed to resolve repo:", err) 1908 - span.RecordError(err) 1909 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 1910 1476 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1911 1477 return 1912 1478 } 1913 1479 1914 - pull, ok := ctx.Value("pull").(*db.Pull) 1480 + pull, ok := r.Context().Value("pull").(*db.Pull) 1915 1481 if !ok { 1916 1482 log.Println("failed to get pull") 1917 - span.SetAttributes(attribute.String("error", "pull_not_in_context")) 1918 1483 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 1919 1484 return 1920 1485 } 1921 1486 1922 - span.SetAttributes( 1923 - attribute.Int("pull.id", pull.PullId), 1924 - attribute.String("pull.owner", pull.OwnerDid), 1925 - attribute.String("target_branch", pull.TargetBranch), 1926 - ) 1927 - 1928 1487 secret, err := db.GetRegistrationKey(s.db, f.Knot) 1929 1488 if err != nil { 1930 1489 log.Printf("no registration key found for domain %s: %s\n", f.Knot, err) 1931 - span.RecordError(err) 1932 - span.SetAttributes(attribute.String("error", "reg_key_not_found")) 1933 1490 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1934 1491 return 1935 1492 } 1936 1493 1937 - ident, err := s.resolver.ResolveIdent(ctx, pull.OwnerDid) 1494 + ident, err := s.resolver.ResolveIdent(r.Context(), pull.OwnerDid) 1938 1495 if err != nil { 1939 1496 log.Printf("resolving identity: %s", err) 1940 - span.RecordError(err) 1941 - span.SetAttributes(attribute.String("error", "resolve_identity_failed")) 1942 1497 w.WriteHeader(http.StatusNotFound) 1943 1498 return 1944 1499 } ··· 1946 1501 email, err := db.GetPrimaryEmail(s.db, pull.OwnerDid) 1947 1502 if err != nil { 1948 1503 log.Printf("failed to get primary email: %s", err) 1949 - span.RecordError(err) 1950 - span.SetAttributes(attribute.String("error", "get_email_failed")) 1951 1504 } 1952 1505 1953 1506 ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 1954 1507 if err != nil { 1955 1508 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 1956 - span.RecordError(err) 1957 - span.SetAttributes(attribute.String("error", "client_creation_failed")) 1958 1509 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1959 1510 return 1960 1511 } ··· 1963 1514 resp, err := ksClient.Merge([]byte(pull.LatestPatch()), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address) 1964 1515 if err != nil { 1965 1516 log.Printf("failed to merge pull request: %s", err) 1966 - span.RecordError(err) 1967 - span.SetAttributes(attribute.String("error", "merge_failed")) 1968 1517 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1969 1518 return 1970 1519 } 1971 1520 1972 - span.SetAttributes(attribute.Int("response.status", resp.StatusCode)) 1973 - 1974 1521 if resp.StatusCode == http.StatusOK { 1975 1522 err := db.MergePull(s.db, f.RepoAt, pull.PullId) 1976 1523 if err != nil { 1977 1524 log.Printf("failed to update pull request status in database: %s", err) 1978 - span.RecordError(err) 1979 - span.SetAttributes(attribute.String("error", "db_update_failed")) 1980 1525 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1981 1526 return 1982 1527 } 1983 1528 s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s/pulls/%d", f.OwnerHandle(), f.RepoName, pull.PullId)) 1984 1529 } else { 1985 1530 log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode) 1986 - span.SetAttributes(attribute.String("error", "non_ok_response")) 1987 1531 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1988 1532 } 1989 1533 } 1990 1534 1991 1535 func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) { 1992 - ctx, span := s.t.TraceStart(r.Context(), "ClosePull") 1993 - defer span.End() 1994 - 1995 - user := s.auth.GetUser(r.WithContext(ctx)) 1536 + user := s.auth.GetUser(r) 1996 1537 1997 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1538 + f, err := s.fullyResolvedRepo(r) 1998 1539 if err != nil { 1999 1540 log.Println("malformed middleware") 2000 - span.RecordError(err) 2001 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 2002 1541 return 2003 1542 } 2004 1543 2005 - pull, ok := ctx.Value("pull").(*db.Pull) 1544 + pull, ok := r.Context().Value("pull").(*db.Pull) 2006 1545 if !ok { 2007 1546 log.Println("failed to get pull") 2008 - span.SetAttributes(attribute.String("error", "pull_not_in_context")) 2009 1547 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 2010 1548 return 2011 1549 } 2012 1550 2013 - span.SetAttributes( 2014 - attribute.Int("pull.id", pull.PullId), 2015 - attribute.String("pull.owner", pull.OwnerDid), 2016 - attribute.String("user.did", user.Did), 2017 - ) 2018 - 2019 1551 // auth filter: only owner or collaborators can close 2020 1552 roles := RolesInRepo(s, user, f) 2021 1553 isCollaborator := roles.IsCollaborator() 2022 1554 isPullAuthor := user.Did == pull.OwnerDid 2023 1555 isCloseAllowed := isCollaborator || isPullAuthor 2024 - 2025 - span.SetAttributes( 2026 - attribute.Bool("is_collaborator", isCollaborator), 2027 - attribute.Bool("is_pull_author", isPullAuthor), 2028 - attribute.Bool("is_close_allowed", isCloseAllowed), 2029 - ) 2030 - 2031 1556 if !isCloseAllowed { 2032 1557 log.Println("failed to close pull") 2033 - span.SetAttributes(attribute.String("error", "unauthorized")) 2034 1558 s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.") 2035 1559 return 2036 1560 } 2037 1561 2038 1562 // Start a transaction 2039 - tx, err := s.db.BeginTx(ctx, nil) 1563 + tx, err := s.db.BeginTx(r.Context(), nil) 2040 1564 if err != nil { 2041 1565 log.Println("failed to start transaction", err) 2042 - span.RecordError(err) 2043 - span.SetAttributes(attribute.String("error", "transaction_start_failed")) 2044 1566 s.pages.Notice(w, "pull-close", "Failed to close pull.") 2045 1567 return 2046 1568 } ··· 2049 1571 err = db.ClosePull(tx, f.RepoAt, pull.PullId) 2050 1572 if err != nil { 2051 1573 log.Println("failed to close pull", err) 2052 - span.RecordError(err) 2053 - span.SetAttributes(attribute.String("error", "db_close_failed")) 2054 1574 s.pages.Notice(w, "pull-close", "Failed to close pull.") 2055 1575 return 2056 1576 } ··· 2058 1578 // Commit the transaction 2059 1579 if err = tx.Commit(); err != nil { 2060 1580 log.Println("failed to commit transaction", err) 2061 - span.RecordError(err) 2062 - span.SetAttributes(attribute.String("error", "transaction_commit_failed")) 2063 1581 s.pages.Notice(w, "pull-close", "Failed to close pull.") 2064 1582 return 2065 1583 } ··· 2069 1587 } 2070 1588 2071 1589 func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) { 2072 - ctx, span := s.t.TraceStart(r.Context(), "ReopenPull") 2073 - defer span.End() 2074 - 2075 - user := s.auth.GetUser(r.WithContext(ctx)) 1590 + user := s.auth.GetUser(r) 2076 1591 2077 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1592 + f, err := s.fullyResolvedRepo(r) 2078 1593 if err != nil { 2079 1594 log.Println("failed to resolve repo", err) 2080 - span.RecordError(err) 2081 - span.SetAttributes(attribute.String("error", "resolve_repo_failed")) 2082 1595 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 2083 1596 return 2084 1597 } 2085 1598 2086 - pull, ok := ctx.Value("pull").(*db.Pull) 1599 + pull, ok := r.Context().Value("pull").(*db.Pull) 2087 1600 if !ok { 2088 1601 log.Println("failed to get pull") 2089 - span.SetAttributes(attribute.String("error", "pull_not_in_context")) 2090 1602 s.pages.Notice(w, "pull-error", "Failed to edit patch. Try again later.") 2091 1603 return 2092 1604 } 2093 1605 2094 - span.SetAttributes( 2095 - attribute.Int("pull.id", pull.PullId), 2096 - attribute.String("pull.owner", pull.OwnerDid), 2097 - attribute.String("user.did", user.Did), 2098 - ) 2099 - 2100 - // auth filter: only owner or collaborators can reopen 1606 + // auth filter: only owner or collaborators can close 2101 1607 roles := RolesInRepo(s, user, f) 2102 1608 isCollaborator := roles.IsCollaborator() 2103 1609 isPullAuthor := user.Did == pull.OwnerDid 2104 - isReopenAllowed := isCollaborator || isPullAuthor 2105 - 2106 - span.SetAttributes( 2107 - attribute.Bool("is_collaborator", isCollaborator), 2108 - attribute.Bool("is_pull_author", isPullAuthor), 2109 - attribute.Bool("is_reopen_allowed", isReopenAllowed), 2110 - ) 2111 - 2112 - if !isReopenAllowed { 2113 - log.Println("failed to reopen pull") 2114 - span.SetAttributes(attribute.String("error", "unauthorized")) 2115 - s.pages.Notice(w, "pull-close", "You are unauthorized to reopen this pull.") 1610 + isCloseAllowed := isCollaborator || isPullAuthor 1611 + if !isCloseAllowed { 1612 + log.Println("failed to close pull") 1613 + s.pages.Notice(w, "pull-close", "You are unauthorized to close this pull.") 2116 1614 return 2117 1615 } 2118 1616 2119 1617 // Start a transaction 2120 - tx, err := s.db.BeginTx(ctx, nil) 1618 + tx, err := s.db.BeginTx(r.Context(), nil) 2121 1619 if err != nil { 2122 1620 log.Println("failed to start transaction", err) 2123 - span.RecordError(err) 2124 - span.SetAttributes(attribute.String("error", "transaction_start_failed")) 2125 1621 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 2126 1622 return 2127 1623 } ··· 2130 1626 err = db.ReopenPull(tx, f.RepoAt, pull.PullId) 2131 1627 if err != nil { 2132 1628 log.Println("failed to reopen pull", err) 2133 - span.RecordError(err) 2134 - span.SetAttributes(attribute.String("error", "db_reopen_failed")) 2135 1629 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 2136 1630 return 2137 1631 } ··· 2139 1633 // Commit the transaction 2140 1634 if err = tx.Commit(); err != nil { 2141 1635 log.Println("failed to commit transaction", err) 2142 - span.RecordError(err) 2143 - span.SetAttributes(attribute.String("error", "transaction_commit_failed")) 2144 1636 s.pages.Notice(w, "pull-reopen", "Failed to reopen pull.") 2145 1637 return 2146 1638 }
+95 -699
appview/state/repo.go
··· 16 16 "strings" 17 17 "time" 18 18 19 - "go.opentelemetry.io/otel/attribute" 20 - "go.opentelemetry.io/otel/codes" 21 19 "tangled.sh/tangled.sh/core/api/tangled" 22 20 "tangled.sh/tangled.sh/core/appview" 23 21 "tangled.sh/tangled.sh/core/appview/auth" ··· 40 38 ) 41 39 42 40 func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { 43 - ctx, span := s.t.TraceStart(r.Context(), "RepoIndex") 44 - defer span.End() 45 - 46 41 ref := chi.URLParam(r, "ref") 47 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 42 + f, err := s.fullyResolvedRepo(r) 48 43 if err != nil { 49 44 log.Println("failed to fully resolve repo", err) 50 - span.RecordError(err) 51 - span.SetStatus(codes.Error, "failed to fully resolve repo") 52 45 return 53 46 } 54 47 55 48 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 56 49 if err != nil { 57 50 log.Printf("failed to create unsigned client for %s", f.Knot) 58 - span.RecordError(err) 59 - span.SetStatus(codes.Error, "failed to create unsigned client") 60 51 s.pages.Error503(w) 61 52 return 62 53 } ··· 65 56 if err != nil { 66 57 s.pages.Error503(w) 67 58 log.Println("failed to reach knotserver", err) 68 - span.RecordError(err) 69 - span.SetStatus(codes.Error, "failed to reach knotserver") 70 59 return 71 60 } 72 61 defer resp.Body.Close() ··· 74 63 body, err := io.ReadAll(resp.Body) 75 64 if err != nil { 76 65 log.Printf("Error reading response body: %v", err) 77 - span.RecordError(err) 78 - span.SetStatus(codes.Error, "error reading response body") 79 66 return 80 67 } 81 68 ··· 83 70 err = json.Unmarshal(body, &result) 84 71 if err != nil { 85 72 log.Printf("Error unmarshalling response body: %v", err) 86 - span.RecordError(err) 87 - span.SetStatus(codes.Error, "error unmarshalling response body") 88 73 return 89 74 } 90 75 ··· 127 112 tagCount := len(result.Tags) 128 113 fileCount := len(result.Files) 129 114 130 - span.SetAttributes( 131 - attribute.Int("commits.count", commitCount), 132 - attribute.Int("branches.count", branchCount), 133 - attribute.Int("tags.count", tagCount), 134 - attribute.Int("files.count", fileCount), 135 - ) 136 - 137 115 commitCount, branchCount, tagCount = balanceIndexItems(commitCount, branchCount, tagCount, fileCount) 138 116 commitsTrunc := result.Commits[:min(commitCount, len(result.Commits))] 139 117 tagsTrunc := result.Tags[:min(tagCount, len(result.Tags))] ··· 144 122 user := s.auth.GetUser(r) 145 123 s.pages.RepoIndexPage(w, pages.RepoIndexParams{ 146 124 LoggedInUser: user, 147 - RepoInfo: f.RepoInfo(ctx, s, user), 125 + RepoInfo: f.RepoInfo(s, user), 148 126 TagMap: tagMap, 149 127 RepoIndexResponse: result, 150 128 CommitsTrunc: commitsTrunc, ··· 156 134 } 157 135 158 136 func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { 159 - ctx, span := s.t.TraceStart(r.Context(), "RepoLog") 160 - defer span.End() 161 - 162 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 137 + f, err := s.fullyResolvedRepo(r) 163 138 if err != nil { 164 139 log.Println("failed to fully resolve repo", err) 165 - span.RecordError(err) 166 - span.SetStatus(codes.Error, "failed to fully resolve repo") 167 140 return 168 141 } 169 142 ··· 176 149 } 177 150 178 151 ref := chi.URLParam(r, "ref") 179 - span.SetAttributes(attribute.Int("page", page), attribute.String("ref", ref)) 180 152 181 153 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 182 154 if err != nil { 183 155 log.Println("failed to create unsigned client", err) 184 - span.RecordError(err) 185 - span.SetStatus(codes.Error, "failed to create unsigned client") 186 156 return 187 157 } 188 158 189 159 resp, err := us.Log(f.OwnerDid(), f.RepoName, ref, page) 190 160 if err != nil { 191 161 log.Println("failed to reach knotserver", err) 192 - span.RecordError(err) 193 - span.SetStatus(codes.Error, "failed to reach knotserver") 194 162 return 195 163 } 196 164 197 165 body, err := io.ReadAll(resp.Body) 198 166 if err != nil { 199 167 log.Printf("error reading response body: %v", err) 200 - span.RecordError(err) 201 - span.SetStatus(codes.Error, "error reading response body") 202 168 return 203 169 } 204 170 ··· 206 172 err = json.Unmarshal(body, &repolog) 207 173 if err != nil { 208 174 log.Println("failed to parse json response", err) 209 - span.RecordError(err) 210 - span.SetStatus(codes.Error, "failed to parse json response") 211 175 return 212 176 } 213 - 214 - span.SetAttributes(attribute.Int("commits.count", len(repolog.Commits))) 215 177 216 178 result, err := us.Tags(f.OwnerDid(), f.RepoName) 217 179 if err != nil { 218 180 log.Println("failed to reach knotserver", err) 219 - span.RecordError(err) 220 - span.SetStatus(codes.Error, "failed to reach knotserver for tags") 221 181 return 222 182 } 223 183 ··· 230 190 tagMap[hash] = append(tagMap[hash], tag.Name) 231 191 } 232 192 233 - span.SetAttributes(attribute.Int("tags.count", len(result.Tags))) 234 - 235 193 user := s.auth.GetUser(r) 236 194 s.pages.RepoLog(w, pages.RepoLogParams{ 237 195 LoggedInUser: user, 238 196 TagMap: tagMap, 239 - RepoInfo: f.RepoInfo(ctx, s, user), 197 + RepoInfo: f.RepoInfo(s, user), 240 198 RepoLogResponse: repolog, 241 199 EmailToDidOrHandle: EmailToDidOrHandle(s, uniqueEmails(repolog.Commits)), 242 200 }) ··· 244 202 } 245 203 246 204 func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) { 247 - ctx, span := s.t.TraceStart(r.Context(), "RepoDescriptionEdit") 248 - defer span.End() 249 - 250 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 205 + f, err := s.fullyResolvedRepo(r) 251 206 if err != nil { 252 207 log.Println("failed to get repo and knot", err) 253 208 w.WriteHeader(http.StatusBadRequest) ··· 256 211 257 212 user := s.auth.GetUser(r) 258 213 s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ 259 - RepoInfo: f.RepoInfo(ctx, s, user), 214 + RepoInfo: f.RepoInfo(s, user), 260 215 }) 261 216 return 262 217 } 263 218 264 219 func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) { 265 - ctx, span := s.t.TraceStart(r.Context(), "RepoDescription") 266 - defer span.End() 267 - 268 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 220 + f, err := s.fullyResolvedRepo(r) 269 221 if err != nil { 270 222 log.Println("failed to get repo and knot", err) 271 - span.RecordError(err) 272 - span.SetStatus(codes.Error, "failed to resolve repo") 273 223 w.WriteHeader(http.StatusBadRequest) 274 224 return 275 225 } ··· 278 228 rkey := repoAt.RecordKey().String() 279 229 if rkey == "" { 280 230 log.Println("invalid aturi for repo", err) 281 - span.RecordError(err) 282 - span.SetStatus(codes.Error, "invalid aturi for repo") 283 231 w.WriteHeader(http.StatusInternalServerError) 284 232 return 285 233 } 286 234 287 235 user := s.auth.GetUser(r) 288 - span.SetAttributes(attribute.String("method", r.Method)) 289 236 290 237 switch r.Method { 291 238 case http.MethodGet: 292 239 s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ 293 - RepoInfo: f.RepoInfo(ctx, s, user), 240 + RepoInfo: f.RepoInfo(s, user), 294 241 }) 295 242 return 296 243 case http.MethodPut: 297 244 user := s.auth.GetUser(r) 298 245 newDescription := r.FormValue("description") 299 - span.SetAttributes(attribute.String("description", newDescription)) 300 246 client, _ := s.auth.AuthorizedClient(r) 301 247 302 248 // optimistic update 303 - err = db.UpdateDescription(ctx, s.db, string(repoAt), newDescription) 249 + err = db.UpdateDescription(s.db, string(repoAt), newDescription) 304 250 if err != nil { 305 - log.Println("failed to perform update-description query", err) 306 - span.RecordError(err) 307 - span.SetStatus(codes.Error, "failed to update description in database") 251 + log.Println("failed to perferom update-description query", err) 308 252 s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 309 253 return 310 254 } ··· 312 256 // this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field 313 257 // 314 258 // SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests 315 - ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoNSID, user.Did, rkey) 259 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey) 316 260 if err != nil { 317 261 // failed to get record 318 - span.RecordError(err) 319 - span.SetStatus(codes.Error, "failed to get record from PDS") 320 262 s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") 321 263 return 322 264 } 323 - 324 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 265 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 325 266 Collection: tangled.RepoNSID, 326 267 Repo: user.Did, 327 268 Rkey: rkey, ··· 338 279 }) 339 280 340 281 if err != nil { 341 - log.Println("failed to perform update-description query", err) 342 - span.RecordError(err) 343 - span.SetStatus(codes.Error, "failed to put record to PDS") 282 + log.Println("failed to perferom update-description query", err) 344 283 // failed to get record 345 284 s.pages.Notice(w, "repo-notice", "Failed to update description, unable to save to PDS.") 346 285 return 347 286 } 348 287 349 - newRepoInfo := f.RepoInfo(ctx, s, user) 288 + newRepoInfo := f.RepoInfo(s, user) 350 289 newRepoInfo.Description = newDescription 351 290 352 291 s.pages.RepoDescriptionFragment(w, pages.RepoDescriptionParams{ ··· 357 296 } 358 297 359 298 func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { 360 - ctx, span := s.t.TraceStart(r.Context(), "RepoCommit") 361 - defer span.End() 362 - 363 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 299 + f, err := s.fullyResolvedRepo(r) 364 300 if err != nil { 365 301 log.Println("failed to fully resolve repo", err) 366 - span.RecordError(err) 367 - span.SetStatus(codes.Error, "failed to fully resolve repo") 368 302 return 369 303 } 370 304 ref := chi.URLParam(r, "ref") ··· 373 307 protocol = "https" 374 308 } 375 309 376 - span.SetAttributes(attribute.String("ref", ref), attribute.String("protocol", protocol)) 377 - 378 310 if !plumbing.IsHash(ref) { 379 - span.SetAttributes(attribute.Bool("invalid_hash", true)) 380 311 s.pages.Error404(w) 381 312 return 382 313 } 383 314 384 - requestURL := fmt.Sprintf("%s://%s/%s/%s/commit/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref) 385 - span.SetAttributes(attribute.String("request_url", requestURL)) 386 - 387 - resp, err := http.Get(requestURL) 315 + resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/commit/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref)) 388 316 if err != nil { 389 317 log.Println("failed to reach knotserver", err) 390 - span.RecordError(err) 391 - span.SetStatus(codes.Error, "failed to reach knotserver") 392 318 return 393 319 } 394 320 395 321 body, err := io.ReadAll(resp.Body) 396 322 if err != nil { 397 323 log.Printf("Error reading response body: %v", err) 398 - span.RecordError(err) 399 - span.SetStatus(codes.Error, "error reading response body") 400 324 return 401 325 } 402 326 ··· 404 328 err = json.Unmarshal(body, &result) 405 329 if err != nil { 406 330 log.Println("failed to parse response:", err) 407 - span.RecordError(err) 408 - span.SetStatus(codes.Error, "failed to parse response") 409 331 return 410 332 } 411 333 412 334 user := s.auth.GetUser(r) 413 335 s.pages.RepoCommit(w, pages.RepoCommitParams{ 414 336 LoggedInUser: user, 415 - RepoInfo: f.RepoInfo(ctx, s, user), 337 + RepoInfo: f.RepoInfo(s, user), 416 338 RepoCommitResponse: result, 417 339 EmailToDidOrHandle: EmailToDidOrHandle(s, []string{result.Diff.Commit.Author.Email}), 418 340 }) ··· 420 342 } 421 343 422 344 func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) { 423 - ctx, span := s.t.TraceStart(r.Context(), "RepoTree") 424 - defer span.End() 425 - 426 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 345 + f, err := s.fullyResolvedRepo(r) 427 346 if err != nil { 428 347 log.Println("failed to fully resolve repo", err) 429 - span.RecordError(err) 430 - span.SetStatus(codes.Error, "failed to fully resolve repo") 431 348 return 432 349 } 433 350 ··· 437 354 if !s.config.Dev { 438 355 protocol = "https" 439 356 } 440 - 441 - span.SetAttributes( 442 - attribute.String("ref", ref), 443 - attribute.String("tree_path", treePath), 444 - attribute.String("protocol", protocol), 445 - ) 446 - 447 - requestURL := fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath) 448 - span.SetAttributes(attribute.String("request_url", requestURL)) 449 - 450 - resp, err := http.Get(requestURL) 357 + resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) 451 358 if err != nil { 452 359 log.Println("failed to reach knotserver", err) 453 - span.RecordError(err) 454 - span.SetStatus(codes.Error, "failed to reach knotserver") 455 360 return 456 361 } 457 362 458 363 body, err := io.ReadAll(resp.Body) 459 364 if err != nil { 460 365 log.Printf("Error reading response body: %v", err) 461 - span.RecordError(err) 462 - span.SetStatus(codes.Error, "error reading response body") 463 366 return 464 367 } 465 368 ··· 467 370 err = json.Unmarshal(body, &result) 468 371 if err != nil { 469 372 log.Println("failed to parse response:", err) 470 - span.RecordError(err) 471 - span.SetStatus(codes.Error, "failed to parse response") 472 373 return 473 374 } 474 375 475 376 // redirects tree paths trying to access a blob; in this case the result.Files is unpopulated, 476 377 // so we can safely redirect to the "parent" (which is the same file). 477 378 if len(result.Files) == 0 && result.Parent == treePath { 478 - redirectURL := fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent) 479 - span.SetAttributes(attribute.String("redirect_url", redirectURL)) 480 - http.Redirect(w, r, redirectURL, http.StatusFound) 379 + http.Redirect(w, r, fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent), http.StatusFound) 481 380 return 482 381 } 483 382 ··· 499 398 BreadCrumbs: breadcrumbs, 500 399 BaseTreeLink: baseTreeLink, 501 400 BaseBlobLink: baseBlobLink, 502 - RepoInfo: f.RepoInfo(ctx, s, user), 401 + RepoInfo: f.RepoInfo(s, user), 503 402 RepoTreeResponse: result, 504 403 }) 505 404 return 506 405 } 507 406 508 407 func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) { 509 - ctx, span := s.t.TraceStart(r.Context(), "RepoTags") 510 - defer span.End() 511 - 512 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 408 + f, err := s.fullyResolvedRepo(r) 513 409 if err != nil { 514 410 log.Println("failed to get repo and knot", err) 515 - span.RecordError(err) 516 - span.SetStatus(codes.Error, "failed to get repo and knot") 517 411 return 518 412 } 519 413 520 414 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 521 415 if err != nil { 522 416 log.Println("failed to create unsigned client", err) 523 - span.RecordError(err) 524 - span.SetStatus(codes.Error, "failed to create unsigned client") 525 417 return 526 418 } 527 419 528 420 result, err := us.Tags(f.OwnerDid(), f.RepoName) 529 421 if err != nil { 530 422 log.Println("failed to reach knotserver", err) 531 - span.RecordError(err) 532 - span.SetStatus(codes.Error, "failed to reach knotserver") 533 423 return 534 424 } 535 425 536 - span.SetAttributes(attribute.Int("tags.count", len(result.Tags))) 537 - 538 426 artifacts, err := db.GetArtifact(s.db, db.Filter("repo_at", f.RepoAt)) 539 427 if err != nil { 540 428 log.Println("failed grab artifacts", err) 541 - span.RecordError(err) 542 - span.SetStatus(codes.Error, "failed to grab artifacts") 543 429 return 544 430 } 545 - 546 - span.SetAttributes(attribute.Int("artifacts.count", len(artifacts))) 547 431 548 432 // convert artifacts to map for easy UI building 549 433 artifactMap := make(map[plumbing.Hash][]db.Artifact) ··· 567 451 } 568 452 } 569 453 570 - span.SetAttributes(attribute.Int("dangling_artifacts.count", len(danglingArtifacts))) 571 - 572 454 user := s.auth.GetUser(r) 573 455 s.pages.RepoTags(w, pages.RepoTagsParams{ 574 456 LoggedInUser: user, 575 - RepoInfo: f.RepoInfo(ctx, s, user), 457 + RepoInfo: f.RepoInfo(s, user), 576 458 RepoTagsResponse: *result, 577 459 ArtifactMap: artifactMap, 578 460 DanglingArtifacts: danglingArtifacts, ··· 581 463 } 582 464 583 465 func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) { 584 - ctx, span := s.t.TraceStart(r.Context(), "RepoBranches") 585 - defer span.End() 586 - 587 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 466 + f, err := s.fullyResolvedRepo(r) 588 467 if err != nil { 589 468 log.Println("failed to get repo and knot", err) 590 - span.RecordError(err) 591 - span.SetStatus(codes.Error, "failed to get repo and knot") 592 469 return 593 470 } 594 471 595 472 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 596 473 if err != nil { 597 474 log.Println("failed to create unsigned client", err) 598 - span.RecordError(err) 599 - span.SetStatus(codes.Error, "failed to create unsigned client") 600 475 return 601 476 } 602 477 603 478 resp, err := us.Branches(f.OwnerDid(), f.RepoName) 604 479 if err != nil { 605 480 log.Println("failed to reach knotserver", err) 606 - span.RecordError(err) 607 - span.SetStatus(codes.Error, "failed to reach knotserver") 608 481 return 609 482 } 610 483 611 484 body, err := io.ReadAll(resp.Body) 612 485 if err != nil { 613 486 log.Printf("Error reading response body: %v", err) 614 - span.RecordError(err) 615 - span.SetStatus(codes.Error, "error reading response body") 616 487 return 617 488 } 618 489 ··· 620 491 err = json.Unmarshal(body, &result) 621 492 if err != nil { 622 493 log.Println("failed to parse response:", err) 623 - span.RecordError(err) 624 - span.SetStatus(codes.Error, "failed to parse response") 625 494 return 626 495 } 627 - 628 - span.SetAttributes(attribute.Int("branches.count", len(result.Branches))) 629 496 630 497 slices.SortFunc(result.Branches, func(a, b types.Branch) int { 631 498 if a.IsDefault { ··· 647 514 user := s.auth.GetUser(r) 648 515 s.pages.RepoBranches(w, pages.RepoBranchesParams{ 649 516 LoggedInUser: user, 650 - RepoInfo: f.RepoInfo(ctx, s, user), 517 + RepoInfo: f.RepoInfo(s, user), 651 518 RepoBranchesResponse: result, 652 519 }) 653 520 return 654 521 } 655 522 656 523 func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { 657 - ctx, span := s.t.TraceStart(r.Context(), "RepoBlob") 658 - defer span.End() 659 - 660 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 524 + f, err := s.fullyResolvedRepo(r) 661 525 if err != nil { 662 526 log.Println("failed to get repo and knot", err) 663 - span.RecordError(err) 664 - span.SetStatus(codes.Error, "failed to get repo and knot") 665 527 return 666 528 } 667 529 ··· 671 533 if !s.config.Dev { 672 534 protocol = "https" 673 535 } 674 - 675 - span.SetAttributes( 676 - attribute.String("ref", ref), 677 - attribute.String("file_path", filePath), 678 - attribute.String("protocol", protocol), 679 - ) 680 - 681 - requestURL := fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath) 682 - span.SetAttributes(attribute.String("request_url", requestURL)) 683 - 684 - resp, err := http.Get(requestURL) 536 + resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) 685 537 if err != nil { 686 538 log.Println("failed to reach knotserver", err) 687 - span.RecordError(err) 688 - span.SetStatus(codes.Error, "failed to reach knotserver") 689 539 return 690 540 } 691 541 692 542 body, err := io.ReadAll(resp.Body) 693 543 if err != nil { 694 544 log.Printf("Error reading response body: %v", err) 695 - span.RecordError(err) 696 - span.SetStatus(codes.Error, "error reading response body") 697 545 return 698 546 } 699 547 ··· 701 549 err = json.Unmarshal(body, &result) 702 550 if err != nil { 703 551 log.Println("failed to parse response:", err) 704 - span.RecordError(err) 705 - span.SetStatus(codes.Error, "failed to parse response") 706 552 return 707 553 } 708 554 ··· 722 568 showRendered = r.URL.Query().Get("code") != "true" 723 569 } 724 570 725 - span.SetAttributes( 726 - attribute.Bool("is_binary", result.IsBinary), 727 - attribute.Bool("show_rendered", showRendered), 728 - attribute.Bool("render_toggle", renderToggle), 729 - ) 730 - 731 571 user := s.auth.GetUser(r) 732 572 s.pages.RepoBlob(w, pages.RepoBlobParams{ 733 573 LoggedInUser: user, 734 - RepoInfo: f.RepoInfo(ctx, s, user), 574 + RepoInfo: f.RepoInfo(s, user), 735 575 RepoBlobResponse: result, 736 576 BreadCrumbs: breadcrumbs, 737 577 ShowRendered: showRendered, ··· 741 581 } 742 582 743 583 func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) { 744 - ctx, span := s.t.TraceStart(r.Context(), "RepoBlobRaw") 745 - defer span.End() 746 - 747 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 584 + f, err := s.fullyResolvedRepo(r) 748 585 if err != nil { 749 586 log.Println("failed to get repo and knot", err) 750 - span.RecordError(err) 751 - span.SetStatus(codes.Error, "failed to get repo and knot") 752 587 return 753 588 } 754 589 ··· 759 594 if !s.config.Dev { 760 595 protocol = "https" 761 596 } 762 - 763 - span.SetAttributes( 764 - attribute.String("ref", ref), 765 - attribute.String("file_path", filePath), 766 - attribute.String("protocol", protocol), 767 - ) 768 - 769 - requestURL := fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath) 770 - span.SetAttributes(attribute.String("request_url", requestURL)) 771 - 772 - resp, err := http.Get(requestURL) 597 + resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) 773 598 if err != nil { 774 599 log.Println("failed to reach knotserver", err) 775 - span.RecordError(err) 776 - span.SetStatus(codes.Error, "failed to reach knotserver") 777 600 return 778 601 } 779 602 780 603 body, err := io.ReadAll(resp.Body) 781 604 if err != nil { 782 605 log.Printf("Error reading response body: %v", err) 783 - span.RecordError(err) 784 - span.SetStatus(codes.Error, "error reading response body") 785 606 return 786 607 } 787 608 ··· 789 610 err = json.Unmarshal(body, &result) 790 611 if err != nil { 791 612 log.Println("failed to parse response:", err) 792 - span.RecordError(err) 793 - span.SetStatus(codes.Error, "failed to parse response") 794 613 return 795 614 } 796 - 797 - span.SetAttributes(attribute.Bool("is_binary", result.IsBinary)) 798 615 799 616 if result.IsBinary { 800 617 w.Header().Set("Content-Type", "application/octet-stream") ··· 808 625 } 809 626 810 627 func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) { 811 - ctx, span := s.t.TraceStart(r.Context(), "AddCollaborator") 812 - defer span.End() 813 - 814 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 628 + f, err := s.fullyResolvedRepo(r) 815 629 if err != nil { 816 630 log.Println("failed to get repo and knot", err) 817 - span.RecordError(err) 818 - span.SetStatus(codes.Error, "failed to get repo and knot") 819 631 return 820 632 } 821 633 822 634 collaborator := r.FormValue("collaborator") 823 635 if collaborator == "" { 824 - span.SetAttributes(attribute.String("error", "malformed_form")) 825 636 http.Error(w, "malformed form", http.StatusBadRequest) 826 637 return 827 638 } 828 639 829 - span.SetAttributes(attribute.String("collaborator", collaborator)) 830 - 831 - collaboratorIdent, err := s.resolver.ResolveIdent(ctx, collaborator) 640 + collaboratorIdent, err := s.resolver.ResolveIdent(r.Context(), collaborator) 832 641 if err != nil { 833 - span.RecordError(err) 834 - span.SetStatus(codes.Error, "failed to resolve collaborator") 835 642 w.Write([]byte("failed to resolve collaborator did to a handle")) 836 643 return 837 644 } 838 645 log.Printf("adding %s to %s\n", collaboratorIdent.Handle.String(), f.Knot) 839 - span.SetAttributes( 840 - attribute.String("collaborator_did", collaboratorIdent.DID.String()), 841 - attribute.String("collaborator_handle", collaboratorIdent.Handle.String()), 842 - ) 843 646 844 647 // TODO: create an atproto record for this 845 648 846 649 secret, err := db.GetRegistrationKey(s.db, f.Knot) 847 650 if err != nil { 848 651 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 849 - span.RecordError(err) 850 - span.SetStatus(codes.Error, "no key found for domain") 851 652 return 852 653 } 853 654 854 655 ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 855 656 if err != nil { 856 657 log.Println("failed to create client to ", f.Knot) 857 - span.RecordError(err) 858 - span.SetStatus(codes.Error, "failed to create signed client") 859 658 return 860 659 } 861 660 862 661 ksResp, err := ksClient.AddCollaborator(f.OwnerDid(), f.RepoName, collaboratorIdent.DID.String()) 863 662 if err != nil { 864 663 log.Printf("failed to make request to %s: %s", f.Knot, err) 865 - span.RecordError(err) 866 - span.SetStatus(codes.Error, "failed to make request to knotserver") 867 664 return 868 665 } 869 666 870 667 if ksResp.StatusCode != http.StatusNoContent { 871 - span.SetAttributes(attribute.Int("status_code", ksResp.StatusCode)) 872 668 w.Write([]byte(fmt.Sprint("knotserver failed to add collaborator: ", err))) 873 669 return 874 670 } 875 671 876 - tx, err := s.db.BeginTx(ctx, nil) 672 + tx, err := s.db.BeginTx(r.Context(), nil) 877 673 if err != nil { 878 674 log.Println("failed to start tx") 879 - span.RecordError(err) 880 - span.SetStatus(codes.Error, "failed to start transaction") 881 675 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 882 676 return 883 677 } ··· 891 685 892 686 err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo()) 893 687 if err != nil { 894 - span.RecordError(err) 895 - span.SetStatus(codes.Error, "failed to add collaborator to enforcer") 896 688 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 897 689 return 898 690 } 899 691 900 - err = db.AddCollaborator(ctx, s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 692 + err = db.AddCollaborator(s.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 901 693 if err != nil { 902 - span.RecordError(err) 903 - span.SetStatus(codes.Error, "failed to add collaborator to database") 904 694 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 905 695 return 906 696 } ··· 908 698 err = tx.Commit() 909 699 if err != nil { 910 700 log.Println("failed to commit changes", err) 911 - span.RecordError(err) 912 - span.SetStatus(codes.Error, "failed to commit transaction") 913 701 http.Error(w, err.Error(), http.StatusInternalServerError) 914 702 return 915 703 } ··· 917 705 err = s.enforcer.E.SavePolicy() 918 706 if err != nil { 919 707 log.Println("failed to update ACLs", err) 920 - span.RecordError(err) 921 - span.SetStatus(codes.Error, "failed to save enforcer policy") 922 708 http.Error(w, err.Error(), http.StatusInternalServerError) 923 709 return 924 710 } 925 711 926 712 w.Write([]byte(fmt.Sprint("added collaborator: ", collaboratorIdent.Handle.String()))) 713 + 927 714 } 928 715 929 716 func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { 930 - ctx, span := s.t.TraceStart(r.Context(), "DeleteRepo") 931 - defer span.End() 932 - 933 717 user := s.auth.GetUser(r) 934 718 935 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 719 + f, err := s.fullyResolvedRepo(r) 936 720 if err != nil { 937 721 log.Println("failed to get repo and knot", err) 938 - span.RecordError(err) 939 - span.SetStatus(codes.Error, "failed to get repo and knot") 940 722 return 941 723 } 942 724 943 - span.SetAttributes( 944 - attribute.String("repo_name", f.RepoName), 945 - attribute.String("knot", f.Knot), 946 - attribute.String("owner_did", f.OwnerDid()), 947 - ) 948 - 949 725 // remove record from pds 950 726 xrpcClient, _ := s.auth.AuthorizedClient(r) 951 727 repoRkey := f.RepoAt.RecordKey().String() 952 - _, err = comatproto.RepoDeleteRecord(ctx, xrpcClient, &comatproto.RepoDeleteRecord_Input{ 728 + _, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{ 953 729 Collection: tangled.RepoNSID, 954 730 Repo: user.Did, 955 731 Rkey: repoRkey, 956 732 }) 957 733 if err != nil { 958 734 log.Printf("failed to delete record: %s", err) 959 - span.RecordError(err) 960 - span.SetStatus(codes.Error, "failed to delete record from PDS") 961 735 s.pages.Notice(w, "settings-delete", "Failed to delete repository from PDS.") 962 736 return 963 737 } 964 738 log.Println("removed repo record ", f.RepoAt.String()) 965 - span.SetAttributes(attribute.String("repo_at", f.RepoAt.String())) 966 739 967 740 secret, err := db.GetRegistrationKey(s.db, f.Knot) 968 741 if err != nil { 969 742 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 970 - span.RecordError(err) 971 - span.SetStatus(codes.Error, "no key found for domain") 972 743 return 973 744 } 974 745 975 746 ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 976 747 if err != nil { 977 748 log.Println("failed to create client to ", f.Knot) 978 - span.RecordError(err) 979 - span.SetStatus(codes.Error, "failed to create client") 980 749 return 981 750 } 982 751 983 752 ksResp, err := ksClient.RemoveRepo(f.OwnerDid(), f.RepoName) 984 753 if err != nil { 985 754 log.Printf("failed to make request to %s: %s", f.Knot, err) 986 - span.RecordError(err) 987 - span.SetStatus(codes.Error, "failed to make request to knotserver") 988 755 return 989 756 } 990 757 991 - span.SetAttributes(attribute.Int("ks_status_code", ksResp.StatusCode)) 992 758 if ksResp.StatusCode != http.StatusNoContent { 993 759 log.Println("failed to remove repo from knot, continuing anyway ", f.Knot) 994 - span.SetAttributes(attribute.Bool("knot_remove_failed", true)) 995 760 } else { 996 761 log.Println("removed repo from knot ", f.Knot) 997 - span.SetAttributes(attribute.Bool("knot_remove_success", true)) 998 762 } 999 763 1000 - tx, err := s.db.BeginTx(ctx, nil) 764 + tx, err := s.db.BeginTx(r.Context(), nil) 1001 765 if err != nil { 1002 766 log.Println("failed to start tx") 1003 - span.RecordError(err) 1004 - span.SetStatus(codes.Error, "failed to start transaction") 1005 767 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 1006 768 return 1007 769 } ··· 1010 772 err = s.enforcer.E.LoadPolicy() 1011 773 if err != nil { 1012 774 log.Println("failed to rollback policies") 1013 - span.RecordError(err) 1014 775 } 1015 776 }() 1016 777 1017 778 // remove collaborator RBAC 1018 779 repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.DidSlashRepo(), f.Knot) 1019 780 if err != nil { 1020 - span.RecordError(err) 1021 - span.SetStatus(codes.Error, "failed to get collaborators") 1022 781 s.pages.Notice(w, "settings-delete", "Failed to remove collaborators") 1023 782 return 1024 783 } 1025 - span.SetAttributes(attribute.Int("collaborators.count", len(repoCollaborators))) 1026 - 1027 784 for _, c := range repoCollaborators { 1028 785 did := c[0] 1029 786 s.enforcer.RemoveCollaborator(did, f.Knot, f.DidSlashRepo()) ··· 1033 790 // remove repo RBAC 1034 791 err = s.enforcer.RemoveRepo(f.OwnerDid(), f.Knot, f.DidSlashRepo()) 1035 792 if err != nil { 1036 - span.RecordError(err) 1037 - span.SetStatus(codes.Error, "failed to remove repo RBAC") 1038 793 s.pages.Notice(w, "settings-delete", "Failed to update RBAC rules") 1039 794 return 1040 795 } 1041 796 1042 797 // remove repo from db 1043 - err = db.RemoveRepo(ctx, tx, f.OwnerDid(), f.RepoName) 798 + err = db.RemoveRepo(tx, f.OwnerDid(), f.RepoName) 1044 799 if err != nil { 1045 - span.RecordError(err) 1046 - span.SetStatus(codes.Error, "failed to remove repo from db") 1047 800 s.pages.Notice(w, "settings-delete", "Failed to update appview") 1048 801 return 1049 802 } ··· 1052 805 err = tx.Commit() 1053 806 if err != nil { 1054 807 log.Println("failed to commit changes", err) 1055 - span.RecordError(err) 1056 - span.SetStatus(codes.Error, "failed to commit transaction") 1057 808 http.Error(w, err.Error(), http.StatusInternalServerError) 1058 809 return 1059 810 } ··· 1061 812 err = s.enforcer.E.SavePolicy() 1062 813 if err != nil { 1063 814 log.Println("failed to update ACLs", err) 1064 - span.RecordError(err) 1065 - span.SetStatus(codes.Error, "failed to save policy") 1066 815 http.Error(w, err.Error(), http.StatusInternalServerError) 1067 816 return 1068 817 } ··· 1071 820 } 1072 821 1073 822 func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { 1074 - ctx, span := s.t.TraceStart(r.Context(), "SetDefaultBranch") 1075 - defer span.End() 1076 - 1077 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 823 + f, err := s.fullyResolvedRepo(r) 1078 824 if err != nil { 1079 825 log.Println("failed to get repo and knot", err) 1080 - span.RecordError(err) 1081 - span.SetStatus(codes.Error, "failed to get repo and knot") 1082 826 return 1083 827 } 1084 828 1085 829 branch := r.FormValue("branch") 1086 830 if branch == "" { 1087 - span.SetAttributes(attribute.Bool("malformed_form", true)) 1088 - span.SetStatus(codes.Error, "malformed form") 1089 831 http.Error(w, "malformed form", http.StatusBadRequest) 1090 832 return 1091 833 } 1092 834 1093 - span.SetAttributes( 1094 - attribute.String("branch", branch), 1095 - attribute.String("repo_name", f.RepoName), 1096 - attribute.String("knot", f.Knot), 1097 - attribute.String("owner_did", f.OwnerDid()), 1098 - ) 1099 - 1100 835 secret, err := db.GetRegistrationKey(s.db, f.Knot) 1101 836 if err != nil { 1102 837 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 1103 - span.RecordError(err) 1104 - span.SetStatus(codes.Error, "no key found for domain") 1105 838 return 1106 839 } 1107 840 1108 841 ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 1109 842 if err != nil { 1110 843 log.Println("failed to create client to ", f.Knot) 1111 - span.RecordError(err) 1112 - span.SetStatus(codes.Error, "failed to create client") 1113 844 return 1114 845 } 1115 846 1116 847 ksResp, err := ksClient.SetDefaultBranch(f.OwnerDid(), f.RepoName, branch) 1117 848 if err != nil { 1118 849 log.Printf("failed to make request to %s: %s", f.Knot, err) 1119 - span.RecordError(err) 1120 - span.SetStatus(codes.Error, "failed to make request to knotserver") 1121 850 return 1122 851 } 1123 852 1124 - span.SetAttributes(attribute.Int("ks_status_code", ksResp.StatusCode)) 1125 853 if ksResp.StatusCode != http.StatusNoContent { 1126 - span.SetStatus(codes.Error, "failed to set default branch") 1127 854 s.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 1128 855 return 1129 856 } ··· 1132 859 } 1133 860 1134 861 func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) { 1135 - ctx, span := s.t.TraceStart(r.Context(), "RepoSettings") 1136 - defer span.End() 1137 - 1138 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 862 + f, err := s.fullyResolvedRepo(r) 1139 863 if err != nil { 1140 864 log.Println("failed to get repo and knot", err) 1141 - span.RecordError(err) 1142 - span.SetStatus(codes.Error, "failed to get repo and knot") 1143 865 return 1144 866 } 1145 867 1146 - span.SetAttributes( 1147 - attribute.String("repo_name", f.RepoName), 1148 - attribute.String("knot", f.Knot), 1149 - attribute.String("owner_did", f.OwnerDid()), 1150 - attribute.String("method", r.Method), 1151 - ) 1152 - 1153 868 switch r.Method { 1154 869 case http.MethodGet: 1155 870 // for now, this is just pubkeys 1156 871 user := s.auth.GetUser(r) 1157 - repoCollaborators, err := f.Collaborators(ctx, s) 872 + repoCollaborators, err := f.Collaborators(r.Context(), s) 1158 873 if err != nil { 1159 874 log.Println("failed to get collaborators", err) 1160 - span.RecordError(err) 1161 - span.SetAttributes(attribute.String("error", "failed_to_get_collaborators")) 1162 875 } 1163 - span.SetAttributes(attribute.Int("collaborators.count", len(repoCollaborators))) 1164 876 1165 877 isCollaboratorInviteAllowed := false 1166 878 if user != nil { ··· 1169 881 isCollaboratorInviteAllowed = true 1170 882 } 1171 883 } 1172 - span.SetAttributes(attribute.Bool("invite_allowed", isCollaboratorInviteAllowed)) 1173 884 1174 885 var branchNames []string 1175 886 var defaultBranch string 1176 887 us, err := NewUnsignedClient(f.Knot, s.config.Dev) 1177 888 if err != nil { 1178 889 log.Println("failed to create unsigned client", err) 1179 - span.RecordError(err) 1180 - span.SetAttributes(attribute.String("error", "failed_to_create_unsigned_client")) 1181 890 } else { 1182 891 resp, err := us.Branches(f.OwnerDid(), f.RepoName) 1183 892 if err != nil { 1184 893 log.Println("failed to reach knotserver", err) 1185 - span.RecordError(err) 1186 - span.SetAttributes(attribute.String("error", "failed_to_reach_knotserver_for_branches")) 1187 894 } else { 1188 895 defer resp.Body.Close() 1189 896 1190 897 body, err := io.ReadAll(resp.Body) 1191 898 if err != nil { 1192 899 log.Printf("Error reading response body: %v", err) 1193 - span.RecordError(err) 1194 - span.SetAttributes(attribute.String("error", "failed_to_read_branches_response")) 1195 900 } else { 1196 901 var result types.RepoBranchesResponse 1197 902 err = json.Unmarshal(body, &result) 1198 903 if err != nil { 1199 904 log.Println("failed to parse response:", err) 1200 - span.RecordError(err) 1201 - span.SetAttributes(attribute.String("error", "failed_to_parse_branches_response")) 1202 905 } else { 1203 906 for _, branch := range result.Branches { 1204 907 branchNames = append(branchNames, branch.Name) 1205 908 } 1206 - span.SetAttributes(attribute.Int("branches.count", len(branchNames))) 1207 909 } 1208 910 } 1209 911 } ··· 1211 913 defaultBranchResp, err := us.DefaultBranch(f.OwnerDid(), f.RepoName) 1212 914 if err != nil { 1213 915 log.Println("failed to reach knotserver", err) 1214 - span.RecordError(err) 1215 - span.SetAttributes(attribute.String("error", "failed_to_reach_knotserver_for_default_branch")) 1216 916 } else { 1217 917 defaultBranch = defaultBranchResp.Branch 1218 - span.SetAttributes(attribute.String("default_branch", defaultBranch)) 1219 918 } 1220 919 } 1221 920 s.pages.RepoSettings(w, pages.RepoSettingsParams{ 1222 921 LoggedInUser: user, 1223 - RepoInfo: f.RepoInfo(ctx, s, user), 922 + RepoInfo: f.RepoInfo(s, user), 1224 923 Collaborators: repoCollaborators, 1225 924 IsCollaboratorInviteAllowed: isCollaboratorInviteAllowed, 1226 925 Branches: branchNames, ··· 1309 1008 return collaborators, nil 1310 1009 } 1311 1010 1312 - func (f *FullyResolvedRepo) RepoInfo(ctx context.Context, s *State, u *auth.User) repoinfo.RepoInfo { 1313 - ctx, span := s.t.TraceStart(ctx, "RepoInfo") 1314 - defer span.End() 1315 - 1011 + func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { 1316 1012 isStarred := false 1317 1013 if u != nil { 1318 1014 isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt)) 1319 - span.SetAttributes(attribute.Bool("is_starred", isStarred)) 1320 1015 } 1321 1016 1322 1017 starCount, err := db.GetStarCount(s.db, f.RepoAt) 1323 1018 if err != nil { 1324 1019 log.Println("failed to get star count for ", f.RepoAt) 1325 - span.RecordError(err) 1326 1020 } 1327 - 1328 1021 issueCount, err := db.GetIssueCount(s.db, f.RepoAt) 1329 1022 if err != nil { 1330 1023 log.Println("failed to get issue count for ", f.RepoAt) 1331 - span.RecordError(err) 1332 1024 } 1333 - 1334 1025 pullCount, err := db.GetPullCount(s.db, f.RepoAt) 1335 1026 if err != nil { 1336 1027 log.Println("failed to get issue count for ", f.RepoAt) 1337 - span.RecordError(err) 1338 1028 } 1339 - 1340 - span.SetAttributes( 1341 - attribute.Int("stats.stars", starCount), 1342 - attribute.Int("stats.issues.open", issueCount.Open), 1343 - attribute.Int("stats.issues.closed", issueCount.Closed), 1344 - attribute.Int("stats.pulls.open", pullCount.Open), 1345 - attribute.Int("stats.pulls.closed", pullCount.Closed), 1346 - attribute.Int("stats.pulls.merged", pullCount.Merged), 1347 - ) 1348 - 1349 - source, err := db.GetRepoSource(ctx, s.db, f.RepoAt) 1029 + source, err := db.GetRepoSource(s.db, f.RepoAt) 1350 1030 if errors.Is(err, sql.ErrNoRows) { 1351 1031 source = "" 1352 1032 } else if err != nil { 1353 1033 log.Println("failed to get repo source for ", f.RepoAt, err) 1354 - span.RecordError(err) 1355 1034 } 1356 1035 1357 1036 var sourceRepo *db.Repo 1358 1037 if source != "" { 1359 - span.SetAttributes(attribute.String("source", source)) 1360 - sourceRepo, err = db.GetRepoByAtUri(ctx, s.db, source) 1038 + sourceRepo, err = db.GetRepoByAtUri(s.db, source) 1361 1039 if err != nil { 1362 1040 log.Println("failed to get repo by at uri", err) 1363 - span.RecordError(err) 1364 1041 } 1365 1042 } 1366 1043 1367 1044 var sourceHandle *identity.Identity 1368 1045 if sourceRepo != nil { 1369 - sourceHandle, err = s.resolver.ResolveIdent(ctx, sourceRepo.Did) 1046 + sourceHandle, err = s.resolver.ResolveIdent(context.Background(), sourceRepo.Did) 1370 1047 if err != nil { 1371 1048 log.Println("failed to resolve source repo", err) 1372 - span.RecordError(err) 1373 - } else if sourceHandle != nil { 1374 - span.SetAttributes(attribute.String("source_handle", sourceHandle.Handle.String())) 1375 1049 } 1376 1050 } 1377 1051 1378 1052 knot := f.Knot 1379 - span.SetAttributes(attribute.String("knot", knot)) 1380 - 1381 1053 var disableFork bool 1382 1054 us, err := NewUnsignedClient(knot, s.config.Dev) 1383 1055 if err != nil { 1384 1056 log.Printf("failed to create unsigned client for %s: %v", knot, err) 1385 - span.RecordError(err) 1386 1057 } else { 1387 1058 resp, err := us.Branches(f.OwnerDid(), f.RepoName) 1388 1059 if err != nil { 1389 1060 log.Printf("failed to get branches for %s/%s: %v", f.OwnerDid(), f.RepoName, err) 1390 - span.RecordError(err) 1391 1061 } else { 1392 1062 defer resp.Body.Close() 1393 1063 body, err := io.ReadAll(resp.Body) 1394 1064 if err != nil { 1395 1065 log.Printf("error reading branch response body: %v", err) 1396 - span.RecordError(err) 1397 1066 } else { 1398 1067 var branchesResp types.RepoBranchesResponse 1399 1068 if err := json.Unmarshal(body, &branchesResp); err != nil { 1400 1069 log.Printf("error parsing branch response: %v", err) 1401 - span.RecordError(err) 1402 1070 } else { 1403 1071 disableFork = false 1404 1072 } ··· 1406 1074 if len(branchesResp.Branches) == 0 { 1407 1075 disableFork = true 1408 1076 } 1409 - span.SetAttributes( 1410 - attribute.Int("branches.count", len(branchesResp.Branches)), 1411 - attribute.Bool("disable_fork", disableFork), 1412 - ) 1413 1077 } 1414 1078 } 1415 1079 } ··· 1441 1105 } 1442 1106 1443 1107 func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 1444 - ctx, span := s.t.TraceStart(r.Context(), "RepoSingleIssue") 1445 - defer span.End() 1446 - 1447 1108 user := s.auth.GetUser(r) 1448 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1109 + f, err := s.fullyResolvedRepo(r) 1449 1110 if err != nil { 1450 1111 log.Println("failed to get repo and knot", err) 1451 - span.RecordError(err) 1452 - span.SetStatus(codes.Error, "failed to resolve repo") 1453 1112 return 1454 1113 } 1455 1114 ··· 1458 1117 if err != nil { 1459 1118 http.Error(w, "bad issue id", http.StatusBadRequest) 1460 1119 log.Println("failed to parse issue id", err) 1461 - span.RecordError(err) 1462 - span.SetStatus(codes.Error, "failed to parse issue id") 1463 1120 return 1464 1121 } 1465 1122 1466 - span.SetAttributes(attribute.Int("issue_id", issueIdInt)) 1467 - 1468 - issue, comments, err := db.GetIssueWithComments(ctx, s.db, f.RepoAt, issueIdInt) 1123 + issue, comments, err := db.GetIssueWithComments(s.db, f.RepoAt, issueIdInt) 1469 1124 if err != nil { 1470 1125 log.Println("failed to get issue and comments", err) 1471 - span.RecordError(err) 1472 - span.SetStatus(codes.Error, "failed to get issue and comments") 1473 1126 s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1474 1127 return 1475 1128 } 1476 1129 1477 - span.SetAttributes( 1478 - attribute.Int("comments.count", len(comments)), 1479 - attribute.String("issue.title", issue.Title), 1480 - attribute.String("issue.owner_did", issue.OwnerDid), 1481 - ) 1482 - 1483 - issueOwnerIdent, err := s.resolver.ResolveIdent(ctx, issue.OwnerDid) 1130 + issueOwnerIdent, err := s.resolver.ResolveIdent(r.Context(), issue.OwnerDid) 1484 1131 if err != nil { 1485 1132 log.Println("failed to resolve issue owner", err) 1486 - span.RecordError(err) 1487 - span.SetStatus(codes.Error, "failed to resolve issue owner") 1488 1133 } 1489 1134 1490 1135 identsToResolve := make([]string, len(comments)) 1491 1136 for i, comment := range comments { 1492 1137 identsToResolve[i] = comment.OwnerDid 1493 1138 } 1494 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 1139 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 1495 1140 didHandleMap := make(map[string]string) 1496 1141 for _, identity := range resolvedIds { 1497 1142 if !identity.Handle.IsInvalidHandle() { ··· 1503 1148 1504 1149 s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 1505 1150 LoggedInUser: user, 1506 - RepoInfo: f.RepoInfo(ctx, s, user), 1151 + RepoInfo: f.RepoInfo(s, user), 1507 1152 Issue: *issue, 1508 1153 Comments: comments, 1509 1154 1510 1155 IssueOwnerHandle: issueOwnerIdent.Handle.String(), 1511 1156 DidHandleMap: didHandleMap, 1512 1157 }) 1158 + 1513 1159 } 1514 1160 1515 1161 func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { 1516 - ctx, span := s.t.TraceStart(r.Context(), "CloseIssue") 1517 - defer span.End() 1518 - 1519 1162 user := s.auth.GetUser(r) 1520 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1163 + f, err := s.fullyResolvedRepo(r) 1521 1164 if err != nil { 1522 1165 log.Println("failed to get repo and knot", err) 1523 - span.RecordError(err) 1524 - span.SetStatus(codes.Error, "failed to resolve repo") 1525 1166 return 1526 1167 } 1527 1168 ··· 1530 1171 if err != nil { 1531 1172 http.Error(w, "bad issue id", http.StatusBadRequest) 1532 1173 log.Println("failed to parse issue id", err) 1533 - span.RecordError(err) 1534 - span.SetStatus(codes.Error, "failed to parse issue id") 1535 1174 return 1536 1175 } 1537 1176 1538 - span.SetAttributes(attribute.Int("issue_id", issueIdInt)) 1539 - 1540 - issue, err := db.GetIssue(ctx, s.db, f.RepoAt, issueIdInt) 1177 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1541 1178 if err != nil { 1542 1179 log.Println("failed to get issue", err) 1543 - span.RecordError(err) 1544 - span.SetStatus(codes.Error, "failed to get issue") 1545 1180 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1546 1181 return 1547 1182 } 1548 1183 1549 - collaborators, err := f.Collaborators(ctx, s) 1184 + collaborators, err := f.Collaborators(r.Context(), s) 1550 1185 if err != nil { 1551 1186 log.Println("failed to fetch repo collaborators: %w", err) 1552 - span.RecordError(err) 1553 - span.SetStatus(codes.Error, "failed to fetch repo collaborators") 1554 1187 } 1555 1188 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 1556 1189 return user.Did == collab.Did 1557 1190 }) 1558 1191 isIssueOwner := user.Did == issue.OwnerDid 1559 1192 1560 - span.SetAttributes( 1561 - attribute.Bool("is_collaborator", isCollaborator), 1562 - attribute.Bool("is_issue_owner", isIssueOwner), 1563 - ) 1564 - 1565 1193 // TODO: make this more granular 1566 1194 if isIssueOwner || isCollaborator { 1195 + 1567 1196 closed := tangled.RepoIssueStateClosed 1568 1197 1569 1198 client, _ := s.auth.AuthorizedClient(r) 1570 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1199 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1571 1200 Collection: tangled.RepoIssueStateNSID, 1572 1201 Repo: user.Did, 1573 1202 Rkey: appview.TID(), ··· 1581 1210 1582 1211 if err != nil { 1583 1212 log.Println("failed to update issue state", err) 1584 - span.RecordError(err) 1585 - span.SetStatus(codes.Error, "failed to update issue state in PDS") 1586 1213 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1587 1214 return 1588 1215 } ··· 1590 1217 err := db.CloseIssue(s.db, f.RepoAt, issueIdInt) 1591 1218 if err != nil { 1592 1219 log.Println("failed to close issue", err) 1593 - span.RecordError(err) 1594 - span.SetStatus(codes.Error, "failed to close issue in database") 1595 1220 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1596 1221 return 1597 1222 } ··· 1600 1225 return 1601 1226 } else { 1602 1227 log.Println("user is not permitted to close issue") 1603 - span.SetAttributes(attribute.Bool("permission_denied", true)) 1604 1228 http.Error(w, "for biden", http.StatusUnauthorized) 1605 1229 return 1606 1230 } 1607 1231 } 1608 1232 1609 1233 func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { 1610 - ctx, span := s.t.TraceStart(r.Context(), "ReopenIssue") 1611 - defer span.End() 1612 - 1613 1234 user := s.auth.GetUser(r) 1614 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1235 + f, err := s.fullyResolvedRepo(r) 1615 1236 if err != nil { 1616 1237 log.Println("failed to get repo and knot", err) 1617 - span.RecordError(err) 1618 - span.SetStatus(codes.Error, "failed to resolve repo") 1619 1238 return 1620 1239 } 1621 1240 ··· 1624 1243 if err != nil { 1625 1244 http.Error(w, "bad issue id", http.StatusBadRequest) 1626 1245 log.Println("failed to parse issue id", err) 1627 - span.RecordError(err) 1628 - span.SetStatus(codes.Error, "failed to parse issue id") 1629 1246 return 1630 1247 } 1631 1248 1632 - span.SetAttributes(attribute.Int("issue_id", issueIdInt)) 1633 - 1634 - issue, err := db.GetIssue(ctx, s.db, f.RepoAt, issueIdInt) 1249 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1635 1250 if err != nil { 1636 1251 log.Println("failed to get issue", err) 1637 - span.RecordError(err) 1638 - span.SetStatus(codes.Error, "failed to get issue") 1639 1252 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") 1640 1253 return 1641 1254 } 1642 1255 1643 - collaborators, err := f.Collaborators(ctx, s) 1256 + collaborators, err := f.Collaborators(r.Context(), s) 1644 1257 if err != nil { 1645 1258 log.Println("failed to fetch repo collaborators: %w", err) 1646 - span.RecordError(err) 1647 - span.SetStatus(codes.Error, "failed to fetch repo collaborators") 1648 1259 } 1649 1260 isCollaborator := slices.ContainsFunc(collaborators, func(collab pages.Collaborator) bool { 1650 1261 return user.Did == collab.Did 1651 1262 }) 1652 1263 isIssueOwner := user.Did == issue.OwnerDid 1653 - 1654 - span.SetAttributes( 1655 - attribute.Bool("is_collaborator", isCollaborator), 1656 - attribute.Bool("is_issue_owner", isIssueOwner), 1657 - ) 1658 1264 1659 1265 if isCollaborator || isIssueOwner { 1660 1266 err := db.ReopenIssue(s.db, f.RepoAt, issueIdInt) 1661 1267 if err != nil { 1662 1268 log.Println("failed to reopen issue", err) 1663 - span.RecordError(err) 1664 - span.SetStatus(codes.Error, "failed to reopen issue") 1665 1269 s.pages.Notice(w, "issue-action", "Failed to reopen issue. Try again later.") 1666 1270 return 1667 1271 } ··· 1669 1273 return 1670 1274 } else { 1671 1275 log.Println("user is not the owner of the repo") 1672 - span.SetAttributes(attribute.Bool("permission_denied", true)) 1673 1276 http.Error(w, "forbidden", http.StatusUnauthorized) 1674 1277 return 1675 1278 } 1676 1279 } 1677 1280 1678 1281 func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { 1679 - ctx, span := s.t.TraceStart(r.Context(), "NewIssueComment") 1680 - defer span.End() 1681 - 1682 1282 user := s.auth.GetUser(r) 1683 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1283 + f, err := s.fullyResolvedRepo(r) 1684 1284 if err != nil { 1685 1285 log.Println("failed to get repo and knot", err) 1686 - span.RecordError(err) 1687 - span.SetStatus(codes.Error, "failed to resolve repo") 1688 1286 return 1689 1287 } 1690 1288 ··· 1693 1291 if err != nil { 1694 1292 http.Error(w, "bad issue id", http.StatusBadRequest) 1695 1293 log.Println("failed to parse issue id", err) 1696 - span.RecordError(err) 1697 - span.SetStatus(codes.Error, "failed to parse issue id") 1698 1294 return 1699 1295 } 1700 1296 1701 - span.SetAttributes( 1702 - attribute.Int("issue_id", issueIdInt), 1703 - attribute.String("method", r.Method), 1704 - ) 1705 - 1706 1297 switch r.Method { 1707 1298 case http.MethodPost: 1708 1299 body := r.FormValue("body") 1709 1300 if body == "" { 1710 - span.SetAttributes(attribute.Bool("missing_body", true)) 1711 1301 s.pages.Notice(w, "issue", "Body is required") 1712 1302 return 1713 1303 } ··· 1715 1305 commentId := mathrand.IntN(1000000) 1716 1306 rkey := appview.TID() 1717 1307 1718 - span.SetAttributes( 1719 - attribute.Int("comment_id", commentId), 1720 - attribute.String("rkey", rkey), 1721 - ) 1722 - 1723 1308 err := db.NewIssueComment(s.db, &db.Comment{ 1724 1309 OwnerDid: user.Did, 1725 1310 RepoAt: f.RepoAt, ··· 1730 1315 }) 1731 1316 if err != nil { 1732 1317 log.Println("failed to create comment", err) 1733 - span.RecordError(err) 1734 - span.SetStatus(codes.Error, "failed to create comment in database") 1735 1318 s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1736 1319 return 1737 1320 } ··· 1742 1325 issueAt, err := db.GetIssueAt(s.db, f.RepoAt, issueIdInt) 1743 1326 if err != nil { 1744 1327 log.Println("failed to get issue at", err) 1745 - span.RecordError(err) 1746 - span.SetStatus(codes.Error, "failed to get issue at") 1747 1328 s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1748 1329 return 1749 1330 } 1750 1331 1751 - span.SetAttributes(attribute.String("issue_at", issueAt)) 1752 - 1753 1332 atUri := f.RepoAt.String() 1754 1333 client, _ := s.auth.AuthorizedClient(r) 1755 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1334 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1756 1335 Collection: tangled.RepoIssueCommentNSID, 1757 1336 Repo: user.Did, 1758 1337 Rkey: rkey, ··· 1769 1348 }) 1770 1349 if err != nil { 1771 1350 log.Println("failed to create comment", err) 1772 - span.RecordError(err) 1773 - span.SetStatus(codes.Error, "failed to create comment in PDS") 1774 1351 s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1775 1352 return 1776 1353 } ··· 1781 1358 } 1782 1359 1783 1360 func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { 1784 - ctx, span := s.t.TraceStart(r.Context(), "IssueComment") 1785 - defer span.End() 1786 - 1787 1361 user := s.auth.GetUser(r) 1788 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1362 + f, err := s.fullyResolvedRepo(r) 1789 1363 if err != nil { 1790 1364 log.Println("failed to get repo and knot", err) 1791 - span.RecordError(err) 1792 - span.SetStatus(codes.Error, "failed to resolve repo") 1793 1365 return 1794 1366 } 1795 1367 ··· 1798 1370 if err != nil { 1799 1371 http.Error(w, "bad issue id", http.StatusBadRequest) 1800 1372 log.Println("failed to parse issue id", err) 1801 - span.RecordError(err) 1802 - span.SetStatus(codes.Error, "failed to parse issue id") 1803 1373 return 1804 1374 } 1805 1375 ··· 1808 1378 if err != nil { 1809 1379 http.Error(w, "bad comment id", http.StatusBadRequest) 1810 1380 log.Println("failed to parse issue id", err) 1811 - span.RecordError(err) 1812 - span.SetStatus(codes.Error, "failed to parse comment id") 1813 1381 return 1814 1382 } 1815 1383 1816 - span.SetAttributes( 1817 - attribute.Int("issue_id", issueIdInt), 1818 - attribute.Int("comment_id", commentIdInt), 1819 - ) 1820 - 1821 - issue, err := db.GetIssue(ctx, s.db, f.RepoAt, issueIdInt) 1384 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1822 1385 if err != nil { 1823 1386 log.Println("failed to get issue", err) 1824 - span.RecordError(err) 1825 - span.SetStatus(codes.Error, "failed to get issue") 1826 1387 s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1827 1388 return 1828 1389 } ··· 1830 1391 comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1831 1392 if err != nil { 1832 1393 http.Error(w, "bad comment id", http.StatusBadRequest) 1833 - span.RecordError(err) 1834 - span.SetStatus(codes.Error, "failed to get comment") 1835 1394 return 1836 1395 } 1837 1396 1838 - identity, err := s.resolver.ResolveIdent(ctx, comment.OwnerDid) 1397 + identity, err := s.resolver.ResolveIdent(r.Context(), comment.OwnerDid) 1839 1398 if err != nil { 1840 1399 log.Println("failed to resolve did") 1841 - span.RecordError(err) 1842 - span.SetStatus(codes.Error, "failed to resolve did") 1843 1400 return 1844 1401 } 1845 1402 ··· 1852 1409 1853 1410 s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 1854 1411 LoggedInUser: user, 1855 - RepoInfo: f.RepoInfo(ctx, s, user), 1412 + RepoInfo: f.RepoInfo(s, user), 1856 1413 DidHandleMap: didHandleMap, 1857 1414 Issue: issue, 1858 1415 Comment: comment, ··· 1860 1417 } 1861 1418 1862 1419 func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { 1863 - ctx, span := s.t.TraceStart(r.Context(), "EditIssueComment") 1864 - defer span.End() 1865 - 1866 1420 user := s.auth.GetUser(r) 1867 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1421 + f, err := s.fullyResolvedRepo(r) 1868 1422 if err != nil { 1869 1423 log.Println("failed to get repo and knot", err) 1870 - span.RecordError(err) 1871 - span.SetStatus(codes.Error, "failed to resolve repo") 1872 1424 return 1873 1425 } 1874 1426 ··· 1877 1429 if err != nil { 1878 1430 http.Error(w, "bad issue id", http.StatusBadRequest) 1879 1431 log.Println("failed to parse issue id", err) 1880 - span.RecordError(err) 1881 - span.SetStatus(codes.Error, "failed to parse issue id") 1882 1432 return 1883 1433 } 1884 1434 ··· 1887 1437 if err != nil { 1888 1438 http.Error(w, "bad comment id", http.StatusBadRequest) 1889 1439 log.Println("failed to parse issue id", err) 1890 - span.RecordError(err) 1891 - span.SetStatus(codes.Error, "failed to parse comment id") 1892 1440 return 1893 1441 } 1894 1442 1895 - span.SetAttributes( 1896 - attribute.Int("issue_id", issueIdInt), 1897 - attribute.Int("comment_id", commentIdInt), 1898 - attribute.String("method", r.Method), 1899 - ) 1900 - 1901 - issue, err := db.GetIssue(ctx, s.db, f.RepoAt, issueIdInt) 1443 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 1902 1444 if err != nil { 1903 1445 log.Println("failed to get issue", err) 1904 - span.RecordError(err) 1905 - span.SetStatus(codes.Error, "failed to get issue") 1906 1446 s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 1907 1447 return 1908 1448 } ··· 1910 1450 comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 1911 1451 if err != nil { 1912 1452 http.Error(w, "bad comment id", http.StatusBadRequest) 1913 - span.RecordError(err) 1914 - span.SetStatus(codes.Error, "failed to get comment") 1915 1453 return 1916 1454 } 1917 1455 1918 1456 if comment.OwnerDid != user.Did { 1919 1457 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 1920 - span.SetAttributes(attribute.Bool("permission_denied", true)) 1921 1458 return 1922 1459 } 1923 1460 ··· 1925 1462 case http.MethodGet: 1926 1463 s.pages.EditIssueCommentFragment(w, pages.EditIssueCommentParams{ 1927 1464 LoggedInUser: user, 1928 - RepoInfo: f.RepoInfo(ctx, s, user), 1465 + RepoInfo: f.RepoInfo(s, user), 1929 1466 Issue: issue, 1930 1467 Comment: comment, 1931 1468 }) ··· 1935 1472 client, _ := s.auth.AuthorizedClient(r) 1936 1473 rkey := comment.Rkey 1937 1474 1938 - span.SetAttributes( 1939 - attribute.String("new_body", newBody), 1940 - attribute.String("rkey", rkey), 1941 - ) 1942 - 1943 1475 // optimistic update 1944 1476 edited := time.Now() 1945 1477 err = db.EditComment(s.db, comment.RepoAt, comment.Issue, comment.CommentId, newBody) 1946 1478 if err != nil { 1947 1479 log.Println("failed to perferom update-description query", err) 1948 - span.RecordError(err) 1949 - span.SetStatus(codes.Error, "failed to edit comment in database") 1950 1480 s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 1951 1481 return 1952 1482 } ··· 1954 1484 // rkey is optional, it was introduced later 1955 1485 if comment.Rkey != "" { 1956 1486 // update the record on pds 1957 - ex, err := comatproto.RepoGetRecord(ctx, client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) 1487 + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) 1958 1488 if err != nil { 1959 1489 // failed to get record 1960 1490 log.Println(err, rkey) 1961 - span.RecordError(err) 1962 - span.SetStatus(codes.Error, "failed to get record from PDS") 1963 1491 s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.") 1964 1492 return 1965 1493 } ··· 1971 1499 createdAt := record["createdAt"].(string) 1972 1500 commentIdInt64 := int64(commentIdInt) 1973 1501 1974 - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1502 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1975 1503 Collection: tangled.RepoIssueCommentNSID, 1976 1504 Repo: user.Did, 1977 1505 Rkey: rkey, ··· 1989 1517 }) 1990 1518 if err != nil { 1991 1519 log.Println(err) 1992 - span.RecordError(err) 1993 - span.SetStatus(codes.Error, "failed to put record to PDS") 1994 1520 } 1995 1521 } 1996 1522 ··· 2004 1530 // return new comment body with htmx 2005 1531 s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 2006 1532 LoggedInUser: user, 2007 - RepoInfo: f.RepoInfo(ctx, s, user), 1533 + RepoInfo: f.RepoInfo(s, user), 2008 1534 DidHandleMap: didHandleMap, 2009 1535 Issue: issue, 2010 1536 Comment: comment, 2011 1537 }) 2012 1538 return 1539 + 2013 1540 } 1541 + 2014 1542 } 2015 1543 2016 1544 func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { 2017 - ctx, span := s.t.TraceStart(r.Context(), "DeleteIssueComment") 2018 - defer span.End() 2019 - 2020 1545 user := s.auth.GetUser(r) 2021 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1546 + f, err := s.fullyResolvedRepo(r) 2022 1547 if err != nil { 2023 1548 log.Println("failed to get repo and knot", err) 2024 - span.RecordError(err) 2025 - span.SetStatus(codes.Error, "failed to resolve repo") 2026 1549 return 2027 1550 } 2028 1551 ··· 2031 1554 if err != nil { 2032 1555 http.Error(w, "bad issue id", http.StatusBadRequest) 2033 1556 log.Println("failed to parse issue id", err) 2034 - span.RecordError(err) 2035 - span.SetStatus(codes.Error, "failed to parse issue id") 2036 1557 return 2037 1558 } 2038 1559 2039 - issue, err := db.GetIssue(ctx, s.db, f.RepoAt, issueIdInt) 1560 + issue, err := db.GetIssue(s.db, f.RepoAt, issueIdInt) 2040 1561 if err != nil { 2041 1562 log.Println("failed to get issue", err) 2042 - span.RecordError(err) 2043 - span.SetStatus(codes.Error, "failed to get issue") 2044 1563 s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 2045 1564 return 2046 1565 } ··· 2050 1569 if err != nil { 2051 1570 http.Error(w, "bad comment id", http.StatusBadRequest) 2052 1571 log.Println("failed to parse issue id", err) 2053 - span.RecordError(err) 2054 - span.SetStatus(codes.Error, "failed to parse comment id") 2055 1572 return 2056 1573 } 2057 1574 2058 - span.SetAttributes( 2059 - attribute.Int("issue_id", issueIdInt), 2060 - attribute.Int("comment_id", commentIdInt), 2061 - ) 2062 - 2063 1575 comment, err := db.GetComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 2064 1576 if err != nil { 2065 1577 http.Error(w, "bad comment id", http.StatusBadRequest) 2066 - span.RecordError(err) 2067 - span.SetStatus(codes.Error, "failed to get comment") 2068 1578 return 2069 1579 } 2070 1580 2071 1581 if comment.OwnerDid != user.Did { 2072 1582 http.Error(w, "you are not the author of this comment", http.StatusUnauthorized) 2073 - span.SetAttributes(attribute.Bool("permission_denied", true)) 2074 1583 return 2075 1584 } 2076 1585 2077 1586 if comment.Deleted != nil { 2078 1587 http.Error(w, "comment already deleted", http.StatusBadRequest) 2079 - span.SetAttributes(attribute.Bool("already_deleted", true)) 2080 1588 return 2081 1589 } 2082 1590 ··· 2085 1593 err = db.DeleteComment(s.db, f.RepoAt, issueIdInt, commentIdInt) 2086 1594 if err != nil { 2087 1595 log.Println("failed to delete comment") 2088 - span.RecordError(err) 2089 - span.SetStatus(codes.Error, "failed to delete comment in database") 2090 1596 s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") 2091 1597 return 2092 1598 } ··· 2094 1600 // delete from pds 2095 1601 if comment.Rkey != "" { 2096 1602 client, _ := s.auth.AuthorizedClient(r) 2097 - _, err = comatproto.RepoDeleteRecord(ctx, client, &comatproto.RepoDeleteRecord_Input{ 1603 + _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 2098 1604 Collection: tangled.GraphFollowNSID, 2099 1605 Repo: user.Did, 2100 1606 Rkey: comment.Rkey, 2101 1607 }) 2102 1608 if err != nil { 2103 1609 log.Println(err) 2104 - span.RecordError(err) 2105 - span.SetStatus(codes.Error, "failed to delete record from PDS") 2106 1610 } 2107 1611 } 2108 1612 ··· 2116 1620 // htmx fragment of comment after deletion 2117 1621 s.pages.SingleIssueCommentFragment(w, pages.SingleIssueCommentParams{ 2118 1622 LoggedInUser: user, 2119 - RepoInfo: f.RepoInfo(ctx, s, user), 1623 + RepoInfo: f.RepoInfo(s, user), 2120 1624 DidHandleMap: didHandleMap, 2121 1625 Issue: issue, 2122 1626 Comment: comment, ··· 2125 1629 } 2126 1630 2127 1631 func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { 2128 - ctx, span := s.t.TraceStart(r.Context(), "RepoIssues") 2129 - defer span.End() 2130 - 2131 1632 params := r.URL.Query() 2132 1633 state := params.Get("state") 2133 1634 isOpen := true ··· 2140 1641 isOpen = true 2141 1642 } 2142 1643 2143 - span.SetAttributes( 2144 - attribute.Bool("is_open", isOpen), 2145 - attribute.String("state_param", state), 2146 - ) 2147 - 2148 1644 page, ok := r.Context().Value("page").(pagination.Page) 2149 1645 if !ok { 2150 1646 log.Println("failed to get page") 2151 - span.SetAttributes(attribute.Bool("page_not_found", true)) 2152 1647 page = pagination.FirstPage() 2153 1648 } 2154 1649 2155 1650 user := s.auth.GetUser(r) 2156 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1651 + f, err := s.fullyResolvedRepo(r) 2157 1652 if err != nil { 2158 1653 log.Println("failed to get repo and knot", err) 2159 - span.RecordError(err) 2160 - span.SetStatus(codes.Error, "failed to resolve repo") 2161 1654 return 2162 1655 } 2163 1656 2164 - issues, err := db.GetIssues(ctx, s.db, f.RepoAt, isOpen, page) 1657 + issues, err := db.GetIssues(s.db, f.RepoAt, isOpen, page) 2165 1658 if err != nil { 2166 1659 log.Println("failed to get issues", err) 2167 - span.RecordError(err) 2168 - span.SetStatus(codes.Error, "failed to get issues") 2169 1660 s.pages.Notice(w, "issues", "Failed to load issues. Try again later.") 2170 1661 return 2171 1662 } 2172 1663 2173 - span.SetAttributes(attribute.Int("issues.count", len(issues))) 2174 - 2175 1664 identsToResolve := make([]string, len(issues)) 2176 1665 for i, issue := range issues { 2177 1666 identsToResolve[i] = issue.OwnerDid 2178 1667 } 2179 - resolvedIds := s.resolver.ResolveIdents(ctx, identsToResolve) 1668 + resolvedIds := s.resolver.ResolveIdents(r.Context(), identsToResolve) 2180 1669 didHandleMap := make(map[string]string) 2181 1670 for _, identity := range resolvedIds { 2182 1671 if !identity.Handle.IsInvalidHandle() { ··· 2188 1677 2189 1678 s.pages.RepoIssues(w, pages.RepoIssuesParams{ 2190 1679 LoggedInUser: s.auth.GetUser(r), 2191 - RepoInfo: f.RepoInfo(ctx, s, user), 1680 + RepoInfo: f.RepoInfo(s, user), 2192 1681 Issues: issues, 2193 1682 DidHandleMap: didHandleMap, 2194 1683 FilteringByOpen: isOpen, ··· 2198 1687 } 2199 1688 2200 1689 func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { 2201 - ctx, span := s.t.TraceStart(r.Context(), "NewIssue") 2202 - defer span.End() 2203 - 2204 1690 user := s.auth.GetUser(r) 2205 1691 2206 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1692 + f, err := s.fullyResolvedRepo(r) 2207 1693 if err != nil { 2208 1694 log.Println("failed to get repo and knot", err) 2209 - span.RecordError(err) 2210 - span.SetStatus(codes.Error, "failed to resolve repo") 2211 1695 return 2212 1696 } 2213 - 2214 - span.SetAttributes(attribute.String("method", r.Method)) 2215 1697 2216 1698 switch r.Method { 2217 1699 case http.MethodGet: 2218 1700 s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{ 2219 1701 LoggedInUser: user, 2220 - RepoInfo: f.RepoInfo(ctx, s, user), 1702 + RepoInfo: f.RepoInfo(s, user), 2221 1703 }) 2222 1704 case http.MethodPost: 2223 1705 title := r.FormValue("title") 2224 1706 body := r.FormValue("body") 2225 1707 2226 - span.SetAttributes( 2227 - attribute.String("title", title), 2228 - attribute.String("body_length", fmt.Sprintf("%d", len(body))), 2229 - ) 2230 - 2231 1708 if title == "" || body == "" { 2232 - span.SetAttributes(attribute.Bool("form_validation_failed", true)) 2233 1709 s.pages.Notice(w, "issues", "Title and body are required") 2234 1710 return 2235 1711 } 2236 1712 2237 - tx, err := s.db.BeginTx(ctx, nil) 1713 + tx, err := s.db.BeginTx(r.Context(), nil) 2238 1714 if err != nil { 2239 - span.RecordError(err) 2240 - span.SetStatus(codes.Error, "failed to begin transaction") 2241 1715 s.pages.Notice(w, "issues", "Failed to create issue, try again later") 2242 1716 return 2243 1717 } ··· 2250 1724 }) 2251 1725 if err != nil { 2252 1726 log.Println("failed to create issue", err) 2253 - span.RecordError(err) 2254 - span.SetStatus(codes.Error, "failed to create issue in database") 2255 1727 s.pages.Notice(w, "issues", "Failed to create issue.") 2256 1728 return 2257 1729 } ··· 2259 1731 issueId, err := db.GetIssueId(s.db, f.RepoAt) 2260 1732 if err != nil { 2261 1733 log.Println("failed to get issue id", err) 2262 - span.RecordError(err) 2263 - span.SetStatus(codes.Error, "failed to get issue id") 2264 1734 s.pages.Notice(w, "issues", "Failed to create issue.") 2265 1735 return 2266 1736 } 2267 1737 2268 - span.SetAttributes(attribute.Int("issue_id", issueId)) 2269 - 2270 1738 client, _ := s.auth.AuthorizedClient(r) 2271 1739 atUri := f.RepoAt.String() 2272 - rkey := appview.TID() 2273 - span.SetAttributes(attribute.String("rkey", rkey)) 2274 - 2275 - resp, err := comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ 1740 + resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 2276 1741 Collection: tangled.RepoIssueNSID, 2277 1742 Repo: user.Did, 2278 - Rkey: rkey, 1743 + Rkey: appview.TID(), 2279 1744 Record: &lexutil.LexiconTypeDecoder{ 2280 1745 Val: &tangled.RepoIssue{ 2281 1746 Repo: atUri, ··· 2288 1753 }) 2289 1754 if err != nil { 2290 1755 log.Println("failed to create issue", err) 2291 - span.RecordError(err) 2292 - span.SetStatus(codes.Error, "failed to create issue in PDS") 2293 1756 s.pages.Notice(w, "issues", "Failed to create issue.") 2294 1757 return 2295 1758 } 2296 1759 2297 - span.SetAttributes(attribute.String("issue_uri", resp.Uri)) 2298 - 2299 1760 err = db.SetIssueAt(s.db, f.RepoAt, issueId, resp.Uri) 2300 1761 if err != nil { 2301 1762 log.Println("failed to set issue at", err) 2302 - span.RecordError(err) 2303 - span.SetStatus(codes.Error, "failed to set issue URI in database") 2304 1763 s.pages.Notice(w, "issues", "Failed to create issue.") 2305 1764 return 2306 1765 } ··· 2311 1770 } 2312 1771 2313 1772 func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { 2314 - ctx, span := s.t.TraceStart(r.Context(), "ForkRepo") 2315 - defer span.End() 2316 - 2317 1773 user := s.auth.GetUser(r) 2318 - f, err := s.fullyResolvedRepo(r.WithContext(ctx)) 1774 + f, err := s.fullyResolvedRepo(r) 2319 1775 if err != nil { 2320 1776 log.Printf("failed to resolve source repo: %v", err) 2321 - span.RecordError(err) 2322 - span.SetStatus(codes.Error, "failed to resolve source repo") 2323 1777 return 2324 1778 } 2325 1779 2326 - span.SetAttributes( 2327 - attribute.String("method", r.Method), 2328 - attribute.String("repo_name", f.RepoName), 2329 - attribute.String("owner_did", f.OwnerDid()), 2330 - attribute.String("knot", f.Knot), 2331 - ) 2332 - 2333 1780 switch r.Method { 2334 1781 case http.MethodGet: 2335 1782 user := s.auth.GetUser(r) 2336 1783 knots, err := s.enforcer.GetDomainsForUser(user.Did) 2337 1784 if err != nil { 2338 - span.RecordError(err) 2339 - span.SetStatus(codes.Error, "failed to get domains for user") 2340 1785 s.pages.Notice(w, "repo", "Invalid user account.") 2341 1786 return 2342 1787 } 2343 1788 2344 - span.SetAttributes(attribute.Int("knots.count", len(knots))) 2345 - 2346 1789 s.pages.ForkRepo(w, pages.ForkRepoParams{ 2347 1790 LoggedInUser: user, 2348 1791 Knots: knots, 2349 - RepoInfo: f.RepoInfo(ctx, s, user), 1792 + RepoInfo: f.RepoInfo(s, user), 2350 1793 }) 2351 1794 2352 1795 case http.MethodPost: 1796 + 2353 1797 knot := r.FormValue("knot") 2354 1798 if knot == "" { 2355 - span.SetAttributes(attribute.Bool("missing_knot", true)) 2356 1799 s.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.") 2357 1800 return 2358 1801 } 2359 1802 2360 - span.SetAttributes(attribute.String("target_knot", knot)) 2361 - 2362 1803 ok, err := s.enforcer.E.Enforce(user.Did, knot, knot, "repo:create") 2363 1804 if err != nil || !ok { 2364 - span.SetAttributes( 2365 - attribute.Bool("permission_denied", true), 2366 - attribute.Bool("enforce_error", err != nil), 2367 - ) 2368 1805 s.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.") 2369 1806 return 2370 1807 } 2371 1808 2372 1809 forkName := fmt.Sprintf("%s", f.RepoName) 2373 - span.SetAttributes(attribute.String("fork_name", forkName)) 2374 1810 2375 1811 // this check is *only* to see if the forked repo name already exists 2376 1812 // in the user's account. 2377 - existingRepo, err := db.GetRepo(ctx, s.db, user.Did, f.RepoName) 1813 + existingRepo, err := db.GetRepo(s.db, user.Did, f.RepoName) 2378 1814 if err != nil { 2379 1815 if errors.Is(err, sql.ErrNoRows) { 2380 1816 // no existing repo with this name found, we can use the name as is 2381 - span.SetAttributes(attribute.Bool("repo_name_available", true)) 2382 1817 } else { 2383 1818 log.Println("error fetching existing repo from db", err) 2384 - span.RecordError(err) 2385 - span.SetStatus(codes.Error, "failed to check for existing repo") 2386 1819 s.pages.Notice(w, "repo", "Failed to fork this repository. Try again later.") 2387 1820 return 2388 1821 } 2389 1822 } else if existingRepo != nil { 2390 1823 // repo with this name already exists, append random string 2391 1824 forkName = fmt.Sprintf("%s-%s", forkName, randomString(3)) 2392 - span.SetAttributes( 2393 - attribute.Bool("repo_name_conflict", true), 2394 - attribute.String("adjusted_fork_name", forkName), 2395 - ) 2396 1825 } 2397 - 2398 1826 secret, err := db.GetRegistrationKey(s.db, knot) 2399 1827 if err != nil { 2400 - span.RecordError(err) 2401 - span.SetStatus(codes.Error, "failed to get registration key") 2402 1828 s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) 2403 1829 return 2404 1830 } 2405 1831 2406 1832 client, err := NewSignedClient(knot, secret, s.config.Dev) 2407 1833 if err != nil { 2408 - span.RecordError(err) 2409 - span.SetStatus(codes.Error, "failed to create signed client") 2410 1834 s.pages.Notice(w, "repo", "Failed to reach knot server.") 2411 1835 return 2412 1836 } ··· 2420 1844 forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.RepoName) 2421 1845 sourceAt := f.RepoAt.String() 2422 1846 2423 - span.SetAttributes( 2424 - attribute.String("fork_source_url", forkSourceUrl), 2425 - attribute.String("source_at", sourceAt), 2426 - ) 2427 - 2428 1847 rkey := appview.TID() 2429 1848 repo := &db.Repo{ 2430 1849 Did: user.Did, ··· 2434 1853 Source: sourceAt, 2435 1854 } 2436 1855 2437 - span.SetAttributes(attribute.String("rkey", rkey)) 2438 - 2439 - tx, err := s.db.BeginTx(ctx, nil) 1856 + tx, err := s.db.BeginTx(r.Context(), nil) 2440 1857 if err != nil { 2441 1858 log.Println(err) 2442 - span.RecordError(err) 2443 - span.SetStatus(codes.Error, "failed to begin transaction") 2444 1859 s.pages.Notice(w, "repo", "Failed to save repository information.") 2445 1860 return 2446 1861 } ··· 2449 1864 err = s.enforcer.E.LoadPolicy() 2450 1865 if err != nil { 2451 1866 log.Println("failed to rollback policies") 2452 - span.RecordError(err) 2453 1867 } 2454 1868 }() 2455 1869 2456 1870 resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName) 2457 1871 if err != nil { 2458 - span.RecordError(err) 2459 - span.SetStatus(codes.Error, "failed to fork repo on knot server") 2460 1872 s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 2461 1873 return 2462 1874 } 2463 - 2464 - span.SetAttributes(attribute.Int("fork_response_status", resp.StatusCode)) 2465 1875 2466 1876 switch resp.StatusCode { 2467 1877 case http.StatusConflict: 2468 - span.SetAttributes(attribute.Bool("name_conflict", true)) 2469 1878 s.pages.Notice(w, "repo", "A repository with that name already exists.") 2470 1879 return 2471 1880 case http.StatusInternalServerError: 2472 - span.SetAttributes(attribute.Bool("server_error", true)) 2473 1881 s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 2474 - return 2475 1882 case http.StatusNoContent: 2476 1883 // continue 2477 1884 } ··· 2479 1886 xrpcClient, _ := s.auth.AuthorizedClient(r) 2480 1887 2481 1888 createdAt := time.Now().Format(time.RFC3339) 2482 - atresp, err := comatproto.RepoPutRecord(ctx, xrpcClient, &comatproto.RepoPutRecord_Input{ 1889 + atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ 2483 1890 Collection: tangled.RepoNSID, 2484 1891 Repo: user.Did, 2485 1892 Rkey: rkey, ··· 2494 1901 }) 2495 1902 if err != nil { 2496 1903 log.Printf("failed to create record: %s", err) 2497 - span.RecordError(err) 2498 - span.SetStatus(codes.Error, "failed to create record in PDS") 2499 1904 s.pages.Notice(w, "repo", "Failed to announce repository creation.") 2500 1905 return 2501 1906 } 2502 1907 log.Println("created repo record: ", atresp.Uri) 2503 - span.SetAttributes(attribute.String("repo_uri", atresp.Uri)) 2504 1908 2505 1909 repo.AtUri = atresp.Uri 2506 - err = db.AddRepo(ctx, tx, repo) 1910 + err = db.AddRepo(tx, repo) 2507 1911 if err != nil { 2508 1912 log.Println(err) 2509 - span.RecordError(err) 2510 - span.SetStatus(codes.Error, "failed to add repo to database") 2511 1913 s.pages.Notice(w, "repo", "Failed to save repository information.") 2512 1914 return 2513 1915 } ··· 2517 1919 err = s.enforcer.AddRepo(user.Did, knot, p) 2518 1920 if err != nil { 2519 1921 log.Println(err) 2520 - span.RecordError(err) 2521 - span.SetStatus(codes.Error, "failed to set up repository permissions") 2522 1922 s.pages.Notice(w, "repo", "Failed to set up repository permissions.") 2523 1923 return 2524 1924 } ··· 2526 1926 err = tx.Commit() 2527 1927 if err != nil { 2528 1928 log.Println("failed to commit changes", err) 2529 - span.RecordError(err) 2530 - span.SetStatus(codes.Error, "failed to commit transaction") 2531 1929 http.Error(w, err.Error(), http.StatusInternalServerError) 2532 1930 return 2533 1931 } ··· 2535 1933 err = s.enforcer.E.SavePolicy() 2536 1934 if err != nil { 2537 1935 log.Println("failed to update ACLs", err) 2538 - span.RecordError(err) 2539 - span.SetStatus(codes.Error, "failed to save policy") 2540 1936 http.Error(w, err.Error(), http.StatusInternalServerError) 2541 1937 return 2542 1938 }
+6 -24
appview/state/repo_util.go
··· 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 13 "github.com/go-chi/chi/v5" 14 14 "github.com/go-git/go-git/v5/plumbing/object" 15 - "go.opentelemetry.io/otel/attribute" 16 15 "tangled.sh/tangled.sh/core/appview/auth" 17 16 "tangled.sh/tangled.sh/core/appview/db" 18 17 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 19 - "tangled.sh/tangled.sh/core/telemetry" 20 18 ) 21 19 22 20 func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) { 23 - ctx := r.Context() 24 - 25 - attrs := telemetry.MapAttrs(map[string]string{ 26 - "repo": chi.URLParam(r, "repo"), 27 - "ref": chi.URLParam(r, "ref"), 28 - }) 29 - 30 - ctx, span := s.t.TraceStart(ctx, "fullyResolvedRepo", attrs...) 31 - defer span.End() 32 - 33 21 repoName := chi.URLParam(r, "repo") 34 - knot, ok := ctx.Value("knot").(string) 22 + knot, ok := r.Context().Value("knot").(string) 35 23 if !ok { 36 24 log.Println("malformed middleware") 37 25 return nil, fmt.Errorf("malformed middleware") 38 26 } 39 - 40 - span.SetAttributes(attribute.String("knot", knot)) 41 - 42 - id, ok := ctx.Value("resolvedId").(identity.Identity) 27 + id, ok := r.Context().Value("resolvedId").(identity.Identity) 43 28 if !ok { 44 29 log.Println("malformed middleware") 45 30 return nil, fmt.Errorf("malformed middleware") 46 31 } 47 32 48 - span.SetAttributes(attribute.String("did", id.DID.String())) 49 - 50 - repoAt, ok := ctx.Value("repoAt").(string) 33 + repoAt, ok := r.Context().Value("repoAt").(string) 51 34 if !ok { 52 35 log.Println("malformed middleware") 53 36 return nil, fmt.Errorf("malformed middleware") ··· 73 56 } 74 57 75 58 ref = defaultBranch.Branch 76 - 77 - span.SetAttributes(attribute.String("default_branch", ref)) 78 59 } 79 60 80 - description, ok := ctx.Value("repoDescription").(string) 81 - addedAt, ok := ctx.Value("repoAddedAt").(string) 61 + // pass through values from the middleware 62 + description, ok := r.Context().Value("repoDescription").(string) 63 + addedAt, ok := r.Context().Value("repoAddedAt").(string) 82 64 83 65 return &FullyResolvedRepo{ 84 66 Knot: knot,
+1 -6
appview/state/router.go
··· 13 13 func (s *State) Router() http.Handler { 14 14 router := chi.NewRouter() 15 15 16 - if s.t != nil { 17 - // top-level telemetry middleware 18 - // router.Use(s.t.RequestDuration()) 19 - // router.Use(s.t.RequestInFlight()) 20 - } 21 - 22 16 router.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { 23 17 pat := chi.URLParam(r, "*") 24 18 if strings.HasPrefix(pat, "did:") || strings.HasPrefix(pat, "@") { ··· 54 48 55 49 func (s *State) UserRouter() http.Handler { 56 50 r := chi.NewRouter() 51 + 57 52 // strip @ from user 58 53 r.Use(StripLeadingAt) 59 54
+9 -69
appview/state/state.go
··· 9 9 "log" 10 10 "log/slog" 11 11 "net/http" 12 - "runtime/debug" 13 12 "strings" 14 13 "time" 15 14 ··· 18 17 lexutil "github.com/bluesky-social/indigo/lex/util" 19 18 securejoin "github.com/cyphar/filepath-securejoin" 20 19 "github.com/go-chi/chi/v5" 21 - "go.opentelemetry.io/otel/attribute" 22 20 "tangled.sh/tangled.sh/core/api/tangled" 23 21 "tangled.sh/tangled.sh/core/appview" 24 22 "tangled.sh/tangled.sh/core/appview/auth" ··· 26 24 "tangled.sh/tangled.sh/core/appview/pages" 27 25 "tangled.sh/tangled.sh/core/jetstream" 28 26 "tangled.sh/tangled.sh/core/rbac" 29 - "tangled.sh/tangled.sh/core/telemetry" 30 27 ) 31 28 32 29 type State struct { ··· 37 34 pages *pages.Pages 38 35 resolver *appview.Resolver 39 36 jc *jetstream.JetstreamClient 40 - t *telemetry.Telemetry 41 37 config *appview.Config 42 38 } 43 39 44 - func Make(ctx context.Context, config *appview.Config) (*State, error) { 40 + func Make(config *appview.Config) (*State, error) { 45 41 d, err := db.Make(config.DbPath) 46 42 if err != nil { 47 43 return nil, err ··· 63 59 64 60 resolver := appview.NewResolver() 65 61 66 - bi, ok := debug.ReadBuildInfo() 67 - var version string 68 - if ok { 69 - version = bi.Main.Version 70 - } else { 71 - version = "v0.0.0-unknown" 72 - } 73 - 74 62 wrapper := db.DbWrapper{d} 75 63 jc, err := jetstream.NewJetstreamClient( 76 64 config.JetstreamEndpoint, ··· 84 72 if err != nil { 85 73 return nil, fmt.Errorf("failed to create jetstream client: %w", err) 86 74 } 87 - err = jc.StartJetstream(ctx, appview.Ingest(wrapper)) 75 + err = jc.StartJetstream(context.Background(), appview.Ingest(wrapper)) 88 76 if err != nil { 89 77 return nil, fmt.Errorf("failed to start jetstream watcher: %w", err) 90 78 } 91 79 92 - var tele *telemetry.Telemetry 93 - if config.EnableTelemetry { 94 - tele, err = telemetry.NewTelemetry(ctx, "appview", version, config.Dev) 95 - if err != nil { 96 - return nil, fmt.Errorf("failed to setup telemetry: %w", err) 97 - } 98 - } 99 - 100 80 state := &State{ 101 81 d, 102 82 auth, ··· 105 85 pgs, 106 86 resolver, 107 87 jc, 108 - tele, 109 88 config, 110 89 } 111 90 ··· 199 178 } 200 179 201 180 func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 202 - ctx, span := s.t.TraceStart(r.Context(), "Timeline") 203 - defer span.End() 204 - 205 181 user := s.auth.GetUser(r) 206 - span.SetAttributes(attribute.String("user.did", user.Did)) 207 182 208 - timeline, err := db.MakeTimeline(ctx, s.db) 183 + timeline, err := db.MakeTimeline(s.db) 209 184 if err != nil { 210 185 log.Println(err) 211 - span.RecordError(err) 212 186 s.pages.Notice(w, "timeline", "Uh oh! Failed to load timeline.") 213 187 } 214 188 ··· 227 201 didsToResolve = append(didsToResolve, ev.Star.StarredByDid, ev.Star.Repo.Did) 228 202 } 229 203 } 230 - span.SetAttributes(attribute.Int("dids.to_resolve.count", len(didsToResolve))) 231 204 232 - resolvedIds := s.resolver.ResolveIdents(ctx, didsToResolve) 205 + resolvedIds := s.resolver.ResolveIdents(r.Context(), didsToResolve) 233 206 didHandleMap := make(map[string]string) 234 207 for _, identity := range resolvedIds { 235 208 if !identity.Handle.IsInvalidHandle() { ··· 238 211 didHandleMap[identity.DID.String()] = identity.DID.String() 239 212 } 240 213 } 241 - span.SetAttributes(attribute.Int("dids.resolved.count", len(resolvedIds))) 242 214 243 215 s.pages.Timeline(w, pages.TimelineParams{ 244 216 LoggedInUser: user, ··· 634 606 } 635 607 636 608 func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { 637 - ctx, span := s.t.TraceStart(r.Context(), "NewRepo") 638 - defer span.End() 639 - 640 609 switch r.Method { 641 610 case http.MethodGet: 642 611 user := s.auth.GetUser(r) 643 - span.SetAttributes(attribute.String("user.did", user.Did)) 644 - span.SetAttributes(attribute.String("request.method", "GET")) 645 - 646 612 knots, err := s.enforcer.GetDomainsForUser(user.Did) 647 613 if err != nil { 648 - span.RecordError(err) 649 614 s.pages.Notice(w, "repo", "Invalid user account.") 650 615 return 651 616 } 652 - span.SetAttributes(attribute.Int("knots.count", len(knots))) 653 617 654 618 s.pages.NewRepo(w, pages.NewRepoParams{ 655 619 LoggedInUser: user, ··· 658 622 659 623 case http.MethodPost: 660 624 user := s.auth.GetUser(r) 661 - span.SetAttributes(attribute.String("user.did", user.Did)) 662 - span.SetAttributes(attribute.String("request.method", "POST")) 663 625 664 626 domain := r.FormValue("domain") 665 627 if domain == "" { 666 628 s.pages.Notice(w, "repo", "Invalid form submission—missing knot domain.") 667 629 return 668 630 } 669 - span.SetAttributes(attribute.String("domain", domain)) 670 631 671 632 repoName := r.FormValue("name") 672 633 if repoName == "" { 673 634 s.pages.Notice(w, "repo", "Repository name cannot be empty.") 674 635 return 675 636 } 676 - span.SetAttributes(attribute.String("repo.name", repoName)) 677 637 678 638 if err := validateRepoName(repoName); err != nil { 679 639 s.pages.Notice(w, "repo", err.Error()) ··· 684 644 if defaultBranch == "" { 685 645 defaultBranch = "main" 686 646 } 687 - span.SetAttributes(attribute.String("repo.default_branch", defaultBranch)) 688 647 689 648 description := r.FormValue("description") 690 649 691 650 ok, err := s.enforcer.E.Enforce(user.Did, domain, domain, "repo:create") 692 651 if err != nil || !ok { 693 - if err != nil { 694 - span.RecordError(err) 695 - } 696 - span.SetAttributes(attribute.Bool("permission.granted", false)) 697 652 s.pages.Notice(w, "repo", "You do not have permission to create a repo in this knot.") 698 653 return 699 654 } 700 - span.SetAttributes(attribute.Bool("permission.granted", true)) 701 655 702 - existingRepo, err := db.GetRepo(ctx, s.db, user.Did, repoName) 656 + existingRepo, err := db.GetRepo(s.db, user.Did, repoName) 703 657 if err == nil && existingRepo != nil { 704 - span.SetAttributes(attribute.Bool("repo.exists", true)) 705 658 s.pages.Notice(w, "repo", fmt.Sprintf("A repo by this name already exists on %s", existingRepo.Knot)) 706 659 return 707 660 } 708 - span.SetAttributes(attribute.Bool("repo.exists", false)) 709 661 710 662 secret, err := db.GetRegistrationKey(s.db, domain) 711 663 if err != nil { 712 - span.RecordError(err) 713 664 s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", domain)) 714 665 return 715 666 } 716 667 717 668 client, err := NewSignedClient(domain, secret, s.config.Dev) 718 669 if err != nil { 719 - span.RecordError(err) 720 670 s.pages.Notice(w, "repo", "Failed to connect to knot server.") 721 671 return 722 672 } ··· 730 680 Description: description, 731 681 } 732 682 733 - rWithCtx := r.WithContext(ctx) 734 - xrpcClient, _ := s.auth.AuthorizedClient(rWithCtx) 683 + xrpcClient, _ := s.auth.AuthorizedClient(r) 735 684 736 685 createdAt := time.Now().Format(time.RFC3339) 737 - atresp, err := comatproto.RepoPutRecord(ctx, xrpcClient, &comatproto.RepoPutRecord_Input{ 686 + atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ 738 687 Collection: tangled.RepoNSID, 739 688 Repo: user.Did, 740 689 Rkey: rkey, ··· 747 696 }}, 748 697 }) 749 698 if err != nil { 750 - span.RecordError(err) 751 699 log.Printf("failed to create record: %s", err) 752 700 s.pages.Notice(w, "repo", "Failed to announce repository creation.") 753 701 return 754 702 } 755 703 log.Println("created repo record: ", atresp.Uri) 756 - span.SetAttributes(attribute.String("repo.uri", atresp.Uri)) 757 704 758 - tx, err := s.db.BeginTx(ctx, nil) 705 + tx, err := s.db.BeginTx(r.Context(), nil) 759 706 if err != nil { 760 - span.RecordError(err) 761 707 log.Println(err) 762 708 s.pages.Notice(w, "repo", "Failed to save repository information.") 763 709 return ··· 772 718 773 719 resp, err := client.NewRepo(user.Did, repoName, defaultBranch) 774 720 if err != nil { 775 - span.RecordError(err) 776 721 s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 777 722 return 778 723 } 779 - span.SetAttributes(attribute.Int("knot_response.status", resp.StatusCode)) 780 724 781 725 switch resp.StatusCode { 782 726 case http.StatusConflict: ··· 789 733 } 790 734 791 735 repo.AtUri = atresp.Uri 792 - err = db.AddRepo(ctx, tx, repo) 736 + err = db.AddRepo(tx, repo) 793 737 if err != nil { 794 - span.RecordError(err) 795 738 log.Println(err) 796 739 s.pages.Notice(w, "repo", "Failed to save repository information.") 797 740 return ··· 801 744 p, _ := securejoin.SecureJoin(user.Did, repoName) 802 745 err = s.enforcer.AddRepo(user.Did, domain, p) 803 746 if err != nil { 804 - span.RecordError(err) 805 747 log.Println(err) 806 748 s.pages.Notice(w, "repo", "Failed to set up repository permissions.") 807 749 return ··· 809 751 810 752 err = tx.Commit() 811 753 if err != nil { 812 - span.RecordError(err) 813 754 log.Println("failed to commit changes", err) 814 755 http.Error(w, err.Error(), http.StatusInternalServerError) 815 756 return ··· 817 758 818 759 err = s.enforcer.E.SavePolicy() 819 760 if err != nil { 820 - span.RecordError(err) 821 761 log.Println("failed to update ACLs", err) 822 762 http.Error(w, err.Error(), http.StatusInternalServerError) 823 763 return
+2 -4
cmd/appview/main.go
··· 14 14 func main() { 15 15 slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) 16 16 17 - ctx := context.Background() 18 - 19 - c, err := appview.LoadConfig(ctx) 17 + c, err := appview.LoadConfig(context.Background()) 20 18 if err != nil { 21 19 log.Println("failed to load config", "error", err) 22 20 return 23 21 } 24 22 25 - state, err := state.Make(ctx, c) 23 + state, err := state.Make(c) 26 24 27 25 if err != nil { 28 26 log.Fatal(err)
+6 -21
go.mod
··· 26 26 github.com/sethvargo/go-envconfig v1.1.0 27 27 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e 28 28 github.com/yuin/goldmark v1.4.13 29 - go.opentelemetry.io/otel v1.35.0 30 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 31 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 32 - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 33 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 34 - go.opentelemetry.io/otel/metric v1.35.0 35 - go.opentelemetry.io/otel/sdk v1.35.0 36 - go.opentelemetry.io/otel/sdk/metric v1.35.0 37 - go.opentelemetry.io/otel/trace v1.35.0 38 29 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 39 30 ) 40 31 ··· 48 39 github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect 49 40 github.com/carlmjohnson/versioninfo v0.22.5 // indirect 50 41 github.com/casbin/govaluate v1.3.0 // indirect 51 - github.com/cenkalti/backoff/v4 v4.3.0 // indirect 52 42 github.com/cespare/xxhash/v2 v2.3.0 // indirect 53 43 github.com/cloudflare/circl v1.6.0 // indirect 54 44 github.com/davecgh/go-spew v1.1.1 // indirect ··· 57 47 github.com/felixge/httpsnoop v1.0.4 // indirect 58 48 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 59 49 github.com/go-git/go-billy/v5 v5.6.2 // indirect 60 - github.com/go-logr/logr v1.4.2 // indirect 50 + github.com/go-logr/logr v1.4.1 // indirect 61 51 github.com/go-logr/stdr v1.2.2 // indirect 62 52 github.com/goccy/go-json v0.10.2 // indirect 63 53 github.com/gogo/protobuf v1.3.2 // indirect 64 54 github.com/gorilla/css v1.0.1 // indirect 65 55 github.com/gorilla/securecookie v1.1.2 // indirect 66 56 github.com/gorilla/websocket v1.5.1 // indirect 67 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect 68 57 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 69 58 github.com/hashicorp/go-retryablehttp v0.7.5 // indirect 70 59 github.com/hashicorp/golang-lru v1.0.2 // indirect ··· 110 99 github.com/xanzy/ssh-agent v0.3.3 // indirect 111 100 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 112 101 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 113 - go.opentelemetry.io/auto/sdk v1.1.0 // indirect 114 102 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect 115 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect 116 - go.opentelemetry.io/proto/otlp v1.5.0 // indirect 103 + go.opentelemetry.io/otel v1.21.0 // indirect 104 + go.opentelemetry.io/otel/metric v1.21.0 // indirect 105 + go.opentelemetry.io/otel/trace v1.21.0 // indirect 117 106 go.uber.org/atomic v1.11.0 // indirect 118 107 go.uber.org/multierr v1.11.0 // indirect 119 108 go.uber.org/zap v1.26.0 // indirect 120 109 golang.org/x/crypto v0.37.0 // indirect 121 110 golang.org/x/net v0.39.0 // indirect 122 - golang.org/x/sys v0.33.0 // indirect 123 - golang.org/x/text v0.24.0 // indirect 111 + golang.org/x/sys v0.32.0 // indirect 124 112 golang.org/x/time v0.5.0 // indirect 125 - google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect 126 - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect 127 - google.golang.org/grpc v1.71.0 // indirect 128 - google.golang.org/protobuf v1.36.5 // indirect 113 + google.golang.org/protobuf v1.34.2 // indirect 129 114 gopkg.in/warnings.v0 v0.1.2 // indirect 130 115 gopkg.in/yaml.v3 v3.0.1 // indirect 131 116 lukechampine.com/blake3 v1.2.1 // indirect
+18 -48
go.sum
··· 42 42 github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= 43 43 github.com/casbin/govaluate v1.3.0 h1:VA0eSY0M2lA86dYd5kPPuNZMUD9QkWnOCnavGrw9myc= 44 44 github.com/casbin/govaluate v1.3.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= 45 - github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 46 - github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 47 45 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 48 46 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 49 47 github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= ··· 84 82 github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= 85 83 github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= 86 84 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 87 - github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 88 - github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 85 + github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 86 + github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 89 87 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 90 88 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 91 89 github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= ··· 95 93 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 96 94 github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 97 95 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 98 - github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 99 - github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 100 96 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 101 - github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 102 - github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 97 + github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 98 + github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 103 99 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 104 100 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 105 101 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= ··· 115 111 github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= 116 112 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 117 113 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 118 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= 119 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= 120 114 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 121 115 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 122 116 github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= ··· 233 227 github.com/resend/resend-go/v2 v2.15.0 h1:B6oMEPf8IEQwn2Ovx/9yymkESLDSeNfLFaNMw+mzHhE= 234 228 github.com/resend/resend-go/v2 v2.15.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ= 235 229 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 236 - github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 237 - github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 230 + github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 231 + github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 238 232 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 239 233 github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 240 234 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= ··· 274 268 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 275 269 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 276 270 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 277 - go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 278 - go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 279 271 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= 280 272 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= 281 - go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= 282 - go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 283 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= 284 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= 285 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= 286 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= 287 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= 288 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= 289 - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k= 290 - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY= 291 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0 h1:T0Ec2E+3YZf5bgTNQVet8iTDW7oIk03tXHq+wkwIDnE= 292 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.35.0/go.mod h1:30v2gqH+vYGJsesLWFov8u47EpYTcIQcBjKpI6pJThg= 293 - go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= 294 - go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= 295 - go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= 296 - go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= 297 - go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= 298 - go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= 299 - go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= 300 - go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= 301 - go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= 302 - go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= 273 + go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= 274 + go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= 275 + go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= 276 + go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= 277 + go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= 278 + go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= 303 279 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 304 280 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 305 281 go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 306 282 go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 307 283 go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 308 - go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 309 - go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 284 + go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= 285 + go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= 310 286 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 311 287 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 312 288 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= ··· 381 357 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 382 358 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 383 359 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 384 - golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 385 - golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 360 + golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 361 + golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 386 362 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 387 363 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 388 364 golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= ··· 419 395 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 420 396 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 421 397 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 422 - google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0= 423 - google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= 424 - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= 425 - google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= 426 - google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= 427 - google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= 428 - google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= 429 - google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 398 + google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 399 + google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 430 400 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 431 401 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 432 402 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-88
telemetry/middleware.go
··· 1 - package telemetry 2 - 3 - import ( 4 - "fmt" 5 - "net/http" 6 - "time" 7 - 8 - "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 9 - otelmetric "go.opentelemetry.io/otel/metric" 10 - "go.opentelemetry.io/otel/semconv/v1.13.0/httpconv" 11 - ) 12 - 13 - func (t *Telemetry) RequestDuration() func(next http.Handler) http.Handler { 14 - const ( 15 - metricNameRequestDurationMs = "request_duration_millis" 16 - metricUnitRequestDurationMs = "ms" 17 - metricDescRequestDurationMs = "Measures the latency of HTTP requests processed by the server, in milliseconds." 18 - ) 19 - histogram, err := t.meter.Int64Histogram( 20 - metricNameRequestDurationMs, 21 - otelmetric.WithDescription(metricDescRequestDurationMs), 22 - otelmetric.WithUnit(metricUnitRequestDurationMs), 23 - ) 24 - if err != nil { 25 - panic(fmt.Sprintf("unable to create %s histogram: %v", metricNameRequestDurationMs, err)) 26 - } 27 - 28 - return func(next http.Handler) http.Handler { 29 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 - // capture the start time of the request 31 - startTime := time.Now() 32 - 33 - // execute next http handler 34 - next.ServeHTTP(w, r) 35 - 36 - // record the request duration 37 - duration := time.Since(startTime) 38 - histogram.Record( 39 - r.Context(), 40 - int64(duration.Milliseconds()), 41 - otelmetric.WithAttributes( 42 - httpconv.ServerRequest(t.serviceName, r)..., 43 - ), 44 - ) 45 - }) 46 - } 47 - } 48 - 49 - func (t *Telemetry) RequestInFlight() func(next http.Handler) http.Handler { 50 - const ( 51 - metricNameRequestInFlight = "request_in_flight" 52 - metricDescRequestInFlight = "Measures the number of concurrent HTTP requests being processed by the server." 53 - metricUnitRequestInFlight = "1" 54 - ) 55 - 56 - // counter to capture requests in flight 57 - counter, err := t.meter.Int64UpDownCounter( 58 - metricNameRequestInFlight, 59 - otelmetric.WithDescription(metricDescRequestInFlight), 60 - otelmetric.WithUnit(metricUnitRequestInFlight), 61 - ) 62 - if err != nil { 63 - panic(fmt.Sprintf("unable to create %s counter: %v", metricNameRequestInFlight, err)) 64 - } 65 - 66 - return func(next http.Handler) http.Handler { 67 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 - attrs := otelmetric.WithAttributes(httpconv.ServerRequest(t.serviceName, r)...) 69 - 70 - // increase the number of requests in flight 71 - counter.Add(r.Context(), 1, attrs) 72 - 73 - // execute next http handler 74 - next.ServeHTTP(w, r) 75 - 76 - // decrease the number of requests in flight 77 - counter.Add(r.Context(), -1, attrs) 78 - }) 79 - } 80 - } 81 - 82 - func (t *Telemetry) WithRouteTag() func(next http.Handler) http.Handler { 83 - return func(next http.Handler) http.Handler { 84 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 85 - otelhttp.WithRouteTag(r.URL.Path, next) 86 - }) 87 - } 88 - }
-65
telemetry/provider.go
··· 1 - package telemetry 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - "time" 7 - 8 - "go.opentelemetry.io/otel" 9 - "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" 10 - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" 11 - "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" 12 - "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" 13 - "go.opentelemetry.io/otel/sdk/metric" 14 - "go.opentelemetry.io/otel/sdk/resource" 15 - "go.opentelemetry.io/otel/sdk/trace" 16 - ) 17 - 18 - func NewTracerProvider(ctx context.Context, res *resource.Resource, isDev bool) (*trace.TracerProvider, error) { 19 - var exporter trace.SpanExporter 20 - var err error 21 - 22 - if isDev { 23 - exporter, err = stdouttrace.New() 24 - if err != nil { 25 - return nil, fmt.Errorf("failed to create stdout trace exporter: %w", err) 26 - } 27 - } else { 28 - exporter, err = otlptracegrpc.New(ctx) 29 - if err != nil { 30 - return nil, fmt.Errorf("failed to create OTLP trace exporter: %w", err) 31 - } 32 - } 33 - 34 - tp := trace.NewTracerProvider( 35 - trace.WithBatcher(exporter, trace.WithBatchTimeout(1*time.Second)), 36 - trace.WithResource(res), 37 - ) 38 - otel.SetTracerProvider(tp) 39 - 40 - return tp, nil 41 - } 42 - 43 - func NewMeterProvider(ctx context.Context, res *resource.Resource, isDev bool) (*metric.MeterProvider, error) { 44 - var exporter metric.Exporter 45 - var err error 46 - 47 - if isDev { 48 - exporter, err = stdoutmetric.New() 49 - if err != nil { 50 - return nil, fmt.Errorf("failed to create stdout metric exporter: %w", err) 51 - } 52 - } else { 53 - exporter, err = otlpmetricgrpc.New(ctx) 54 - if err != nil { 55 - return nil, fmt.Errorf("failed to create OTLP metric exporter: %w", err) 56 - } 57 - } 58 - 59 - mp := metric.NewMeterProvider( 60 - metric.WithReader(metric.NewPeriodicReader(exporter, metric.WithInterval(10*time.Second))), 61 - metric.WithResource(res), 62 - ) 63 - otel.SetMeterProvider(mp) 64 - return mp, nil 65 - }
-76
telemetry/telemetry.go
··· 1 - package telemetry 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - 7 - "go.opentelemetry.io/otel/attribute" 8 - otelmetric "go.opentelemetry.io/otel/metric" 9 - "go.opentelemetry.io/otel/sdk/metric" 10 - "go.opentelemetry.io/otel/sdk/resource" 11 - "go.opentelemetry.io/otel/sdk/trace" 12 - semconv "go.opentelemetry.io/otel/semconv/v1.17.0" 13 - oteltrace "go.opentelemetry.io/otel/trace" 14 - ) 15 - 16 - type Telemetry struct { 17 - tp *trace.TracerProvider 18 - mp *metric.MeterProvider 19 - 20 - meter otelmetric.Meter 21 - tracer oteltrace.Tracer 22 - 23 - serviceName string 24 - serviceVersion string 25 - } 26 - 27 - func NewTelemetry(ctx context.Context, serviceName, serviceVersion string, isDev bool) (*Telemetry, error) { 28 - res := resource.NewWithAttributes( 29 - semconv.SchemaURL, 30 - semconv.ServiceName(serviceName), 31 - semconv.ServiceVersion(serviceVersion), 32 - ) 33 - 34 - tp, err := NewTracerProvider(ctx, res, isDev) 35 - if err != nil { 36 - return nil, err 37 - } 38 - 39 - // mp, err := NewMeterProvider(ctx, res, isDev) 40 - // if err != nil { 41 - // return nil, err 42 - // } 43 - 44 - return &Telemetry{ 45 - tp: tp, 46 - //mp: mp, 47 - 48 - //meter: mp.Meter(serviceName), 49 - tracer: tp.Tracer(serviceVersion), 50 - 51 - serviceName: serviceName, 52 - serviceVersion: serviceVersion, 53 - }, nil 54 - } 55 - 56 - func (t *Telemetry) Meter() otelmetric.Meter { 57 - return t.meter 58 - } 59 - 60 - func (t *Telemetry) Tracer() oteltrace.Tracer { 61 - return t.tracer 62 - } 63 - 64 - func (t *Telemetry) TraceStart(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, oteltrace.Span) { 65 - ctx, span := t.tracer.Start(ctx, name) 66 - span.SetAttributes(attrs...) 67 - return ctx, span 68 - } 69 - 70 - func MapAttrs[T any](attrs map[string]T) []attribute.KeyValue { 71 - var result []attribute.KeyValue 72 - for k, v := range attrs { 73 - result = append(result, attribute.Key(k).String(fmt.Sprintf("%v", v))) 74 - } 75 - return result 76 - }