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 8a9ceca3 1a7739de

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