Monorepo for Tangled tangled.org

appview: make default label defs configurable

hard-coded labels make hard to setup local sandboxed environment as the
appview won't run until we fill all 5 definitions under TangledDid.

so make them configurable with `TANGLED_LABEL_DEFAULTS` which is a list
of aturis delimitted by , character

we can have any number/kind of default labels but gfi is required for
`/good-first-issue` page

Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me 3e73d585 41dcabc7

verified
Changed files
+39 -54
appview
config
models
repo
state
+6
appview/config/config.go
··· 78 78 TurnstileSecretKey string `env:"TURNSTILE_SECRET_KEY"` 79 79 } 80 80 81 + type LabelConfig struct { 82 + DefaultLabelDefs []string `env:"DEFAULTS"` // delimiter=, 83 + GoodFirstIssue string `env:"GFI, default=at://did:plc:wshs7t2adsemcrrd4snkeqli/sh.tangled.label.definition/good-first-issue"` 84 + } 85 + 81 86 func (cfg RedisConfig) ToURL() string { 82 87 u := &url.URL{ 83 88 Scheme: "redis", ··· 105 110 Redis RedisConfig `env:",prefix=TANGLED_REDIS_"` 106 111 Pds PdsConfig `env:",prefix=TANGLED_PDS_"` 107 112 Cloudflare Cloudflare `env:",prefix=TANGLED_CLOUDFLARE_"` 113 + Label LabelConfig `env:",prefix=TANGLED_LABEL_"` 108 114 } 109 115 110 116 func LoadConfig(ctx context.Context) (*Config, error) {
+25 -43
appview/models/label.go
··· 14 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 15 "github.com/bluesky-social/indigo/xrpc" 16 16 "tangled.org/core/api/tangled" 17 - "tangled.org/core/consts" 18 17 "tangled.org/core/idresolver" 19 18 ) 20 19 ··· 461 460 return result 462 461 } 463 462 464 - var ( 465 - LabelWontfix = fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "wontfix") 466 - LabelDuplicate = fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "duplicate") 467 - LabelAssignee = fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "assignee") 468 - LabelGoodFirstIssue = fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "good-first-issue") 469 - LabelDocumentation = fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "documentation") 470 - ) 463 + func FetchLabelDefs(r *idresolver.Resolver, aturis []string) ([]LabelDefinition, error) { 464 + var labelDefs []LabelDefinition 465 + ctx := context.Background() 471 466 472 - func DefaultLabelDefs() []string { 473 - return []string{ 474 - LabelWontfix, 475 - LabelDuplicate, 476 - LabelAssignee, 477 - LabelGoodFirstIssue, 478 - LabelDocumentation, 479 - } 480 - } 467 + for _, dl := range aturis { 468 + atUri, err := syntax.ParseATURI(dl) 469 + if err != nil { 470 + return nil, fmt.Errorf("failed to parse AT-URI %s: %v", dl, err) 471 + } 472 + if atUri.Collection() != tangled.LabelDefinitionNSID { 473 + return nil, fmt.Errorf("expected AT-URI pointing %s collection: %s", tangled.LabelDefinitionNSID, atUri) 474 + } 481 475 482 - func FetchDefaultDefs(r *idresolver.Resolver) ([]LabelDefinition, error) { 483 - resolved, err := r.ResolveIdent(context.Background(), consts.TangledDid) 484 - if err != nil { 485 - return nil, fmt.Errorf("failed to resolve tangled.sh DID %s: %v", consts.TangledDid, err) 486 - } 487 - pdsEndpoint := resolved.PDSEndpoint() 488 - if pdsEndpoint == "" { 489 - return nil, fmt.Errorf("no PDS endpoint found for tangled.sh DID %s", consts.TangledDid) 490 - } 491 - client := &xrpc.Client{ 492 - Host: pdsEndpoint, 493 - } 476 + owner, err := r.ResolveIdent(ctx, atUri.Authority().String()) 477 + if err != nil { 478 + return nil, fmt.Errorf("failed to resolve default label owner DID %s: %v", atUri.Authority(), err) 479 + } 494 480 495 - var labelDefs []LabelDefinition 481 + xrpcc := xrpc.Client{ 482 + Host: owner.PDSEndpoint(), 483 + } 496 484 497 - for _, dl := range DefaultLabelDefs() { 498 - atUri := syntax.ATURI(dl) 499 - parsedUri, err := syntax.ParseATURI(string(atUri)) 500 - if err != nil { 501 - return nil, fmt.Errorf("failed to parse AT-URI %s: %v", atUri, err) 502 - } 503 485 record, err := atproto.RepoGetRecord( 504 - context.Background(), 505 - client, 486 + ctx, 487 + &xrpcc, 506 488 "", 507 - parsedUri.Collection().String(), 508 - parsedUri.Authority().String(), 509 - parsedUri.RecordKey().String(), 489 + atUri.Collection().String(), 490 + atUri.Authority().String(), 491 + atUri.RecordKey().String(), 510 492 ) 511 493 if err != nil { 512 494 return nil, fmt.Errorf("failed to get record for %s: %v", atUri, err) ··· 526 508 } 527 509 528 510 labelDef, err := LabelDefinitionFromRecord( 529 - parsedUri.Authority().String(), 530 - parsedUri.RecordKey().String(), 511 + atUri.Authority().String(), 512 + atUri.RecordKey().String(), 531 513 labelRecord, 532 514 ) 533 515 if err != nil {
+2 -2
appview/repo/repo.go
··· 1974 1974 return 1975 1975 } 1976 1976 1977 - defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", models.DefaultLabelDefs())) 1977 + defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", rp.config.Label.DefaultLabelDefs)) 1978 1978 if err != nil { 1979 1979 l.Error("failed to fetch labels", "err", err) 1980 1980 rp.pages.Error503(w) ··· 2253 2253 Source: sourceAt, 2254 2254 Description: f.Repo.Description, 2255 2255 Created: time.Now(), 2256 - Labels: models.DefaultLabelDefs(), 2256 + Labels: rp.config.Label.DefaultLabelDefs, 2257 2257 } 2258 2258 record := repo.AsRecord() 2259 2259
+1 -3
appview/state/gfi.go
··· 1 1 package state 2 2 3 3 import ( 4 - "fmt" 5 4 "log" 6 5 "net/http" 7 6 "sort" 8 7 9 8 "github.com/bluesky-social/indigo/atproto/syntax" 10 - "tangled.org/core/api/tangled" 11 9 "tangled.org/core/appview/db" 12 10 "tangled.org/core/appview/models" 13 11 "tangled.org/core/appview/pages" ··· 20 18 21 19 page := pagination.FromContext(r.Context()) 22 20 23 - goodFirstIssueLabel := fmt.Sprintf("at://%s/%s/%s", consts.TangledDid, tangled.LabelDefinitionNSID, "good-first-issue") 21 + goodFirstIssueLabel := s.config.Label.GoodFirstIssue 24 22 25 23 gfiLabelDef, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", goodFirstIssueLabel)) 26 24 if err != nil {
+5 -6
appview/state/state.go
··· 121 121 return nil, fmt.Errorf("failed to create jetstream client: %w", err) 122 122 } 123 123 124 - if err := BackfillDefaultDefs(d, res); err != nil { 124 + if err := BackfillDefaultDefs(d, res, config.Label.DefaultLabelDefs); err != nil { 125 125 return nil, fmt.Errorf("failed to backfill default label defs: %w", err) 126 126 } 127 127 ··· 284 284 return 285 285 } 286 286 287 - gfiLabel, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", models.LabelGoodFirstIssue)) 287 + gfiLabel, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", s.config.Label.GoodFirstIssue)) 288 288 if err != nil { 289 289 // non-fatal 290 290 } ··· 506 506 Rkey: rkey, 507 507 Description: description, 508 508 Created: time.Now(), 509 - Labels: models.DefaultLabelDefs(), 509 + Labels: s.config.Label.DefaultLabelDefs, 510 510 } 511 511 record := repo.AsRecord() 512 512 ··· 648 648 return err 649 649 } 650 650 651 - func BackfillDefaultDefs(e db.Execer, r *idresolver.Resolver) error { 652 - defaults := models.DefaultLabelDefs() 651 + func BackfillDefaultDefs(e db.Execer, r *idresolver.Resolver, defaults []string) error { 653 652 defaultLabels, err := db.GetLabelDefinitions(e, db.FilterIn("at_uri", defaults)) 654 653 if err != nil { 655 654 return err ··· 659 658 return nil 660 659 } 661 660 662 - labelDefs, err := models.FetchDefaultDefs(r) 661 + labelDefs, err := models.FetchLabelDefs(r, defaults) 663 662 if err != nil { 664 663 return err 665 664 }