Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview/db: support DID formats in labels

Signed-off-by: oppiliappan <me@oppi.li>

+143 -16
+10 -10
appview/db/label.go
··· 6 6 "encoding/hex" 7 7 "errors" 8 8 "fmt" 9 - "log" 10 9 "maps" 11 10 "slices" 12 11 "strings" ··· 79 80 80 81 func (vt ValueType) IsEnumType() bool { 81 82 return len(vt.Enum) > 0 83 + } 84 + 85 + func (vt ValueType) IsDidFormat() bool { 86 + return vt.Format == ValueTypeFormatDid 87 + } 88 + 89 + func (vt ValueType) IsAnyFormat() bool { 90 + return vt.Format == ValueTypeFormatAny 82 91 } 83 92 84 93 type LabelDefinition struct { ··· 602 595 results[subject] = state 603 596 } 604 597 605 - log.Println("results for get labels", "s", results) 606 - 607 598 return results, nil 608 599 } 609 600 ··· 636 631 } 637 632 638 633 type LabelApplicationCtx struct { 639 - defs map[string]*LabelDefinition // labelAt -> labelDef 634 + Defs map[string]*LabelDefinition // labelAt -> labelDef 640 635 } 641 636 642 637 var ( ··· 658 653 } 659 654 660 655 func (c *LabelApplicationCtx) ApplyLabelOp(state LabelState, op LabelOp) error { 661 - def := c.defs[op.OperandKey] 656 + def := c.Defs[op.OperandKey] 662 657 663 658 switch op.Operation { 664 659 case LabelOperationAdd: ··· 718 713 for _, o := range ops { 719 714 _ = c.ApplyLabelOp(state, o) 720 715 } 721 - } 722 - 723 - type Label struct { 724 - def *LabelDefinition 725 - val set 726 716 }
+5 -1
appview/issues/issues.go
··· 92 92 userReactions = db.GetReactionStatusMap(rp.db, user.Did, issue.AtUri()) 93 93 } 94 94 95 - labelDefs, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", f.Repo.Labels)) 95 + labelDefs, err := db.GetLabelDefinitions( 96 + rp.db, 97 + db.FilterIn("at_uri", f.Repo.Labels), 98 + db.FilterEq("scope", tangled.RepoIssueNSID), 99 + ) 96 100 if err != nil { 97 101 log.Println("failed to fetch labels", err) 98 102 rp.pages.Error503(w)
+14 -2
appview/pages/templates/labels/fragments/label.html
··· 1 1 {{ define "labels/fragments/label" }} 2 2 {{ $d := .def }} 3 3 {{ $v := .val }} 4 - <span class="flex items-center gap-2 font-normal normal-case rounded py-1 px-2 border border-gray-200 dark:border-gray-700 text-sm"> 4 + <span class="flex items-center gap-2 font-normal normal-case rounded py-1 px-2 border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm"> 5 5 {{ template "repo/fragments/colorBall" (dict "color" $d.GetColor) }} 6 - {{ $d.Name }}{{ if not $d.ValueType.IsNull }}/{{ $v }}{{ end }} 6 + {{ $d.Name }}{{ if not $d.ValueType.IsNull }}/{{ template "labelVal" (dict "def" $d "val" $v) }}{{ end }} 7 7 </span> 8 + {{ end }} 9 + 10 + 11 + {{ define "labelVal" }} 12 + {{ $d := .def }} 13 + {{ $v := .val }} 14 + 15 + {{ if $d.ValueType.IsDidFormat }} 16 + {{ resolve $v }} 17 + {{ else }} 18 + {{ $v }} 19 + {{ end }} 8 20 {{ end }}
+7 -1
appview/repo/repo.go
··· 987 987 // get form values for label definition 988 988 name := r.FormValue("name") 989 989 concreteType := r.FormValue("valueType") 990 + valueFormat := r.FormValue("valueFormat") 990 991 enumValues := r.FormValue("enumValues") 991 992 scope := r.FormValue("scope") 992 993 color := r.FormValue("color") ··· 1000 999 } 1001 1000 } 1002 1001 1002 + format := db.ValueTypeFormatAny 1003 + if valueFormat == "did" { 1004 + format = db.ValueTypeFormatDid 1005 + } 1006 + 1003 1007 valueType := db.ValueType{ 1004 1008 Type: db.ConcreteType(concreteType), 1005 - Format: db.ValueTypeFormatAny, 1009 + Format: format, 1006 1010 Enum: variants, 1007 1011 } 1008 1012
+1 -1
appview/state/state.go
··· 78 78 cache := cache.New(config.Redis.Addr) 79 79 sess := session.New(cache) 80 80 oauth := oauth.NewOAuth(config, sess) 81 - validator := validator.New(d) 81 + validator := validator.New(d, res) 82 82 83 83 posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint}) 84 84 if err != nil {
+102
appview/validator/label.go
··· 1 1 package validator 2 2 3 3 import ( 4 + "context" 4 5 "fmt" 5 6 "regexp" 6 7 "strings" ··· 72 71 color = strings.ToUpper(color) 73 72 label.Color = &color 74 73 } 74 + } 75 + 76 + return nil 77 + } 78 + 79 + func (v *Validator) ValidateLabelOp(labelDef *db.LabelDefinition, labelOp *db.LabelOp) error { 80 + if labelDef == nil { 81 + return fmt.Errorf("label definition is required") 82 + } 83 + if labelOp == nil { 84 + return fmt.Errorf("label operation is required") 85 + } 86 + 87 + expectedKey := labelDef.AtUri().String() 88 + if labelOp.OperandKey != expectedKey { 89 + return fmt.Errorf("operand key %q does not match label definition URI %q", labelOp.OperandKey, expectedKey) 90 + } 91 + 92 + if labelOp.Operation != db.LabelOperationAdd && labelOp.Operation != db.LabelOperationDel { 93 + return fmt.Errorf("invalid operation: %q (must be 'add' or 'del')", labelOp.Operation) 94 + } 95 + 96 + if labelOp.Subject == "" { 97 + return fmt.Errorf("subject URI is required") 98 + } 99 + if _, err := syntax.ParseATURI(string(labelOp.Subject)); err != nil { 100 + return fmt.Errorf("invalid subject URI: %w", err) 101 + } 102 + 103 + if err := v.validateOperandValue(labelDef, labelOp); err != nil { 104 + return fmt.Errorf("invalid operand value: %w", err) 105 + } 106 + 107 + // Validate performed time is not zero/invalid 108 + if labelOp.PerformedAt.IsZero() { 109 + return fmt.Errorf("performed_at timestamp is required") 110 + } 111 + 112 + return nil 113 + } 114 + 115 + func (v *Validator) validateOperandValue(labelDef *db.LabelDefinition, labelOp *db.LabelOp) error { 116 + valueType := labelDef.ValueType 117 + 118 + switch valueType.Type { 119 + case db.ConcreteTypeNull: 120 + // For null type, value should be empty 121 + if labelOp.OperandValue != "null" { 122 + return fmt.Errorf("null type requires empty value, got %q", labelOp.OperandValue) 123 + } 124 + 125 + case db.ConcreteTypeString: 126 + // For string type, validate enum constraints if present 127 + if valueType.IsEnumType() { 128 + if !slices.Contains(valueType.Enum, labelOp.OperandValue) { 129 + return fmt.Errorf("value %q is not in allowed enum values %v", labelOp.OperandValue, valueType.Enum) 130 + } 131 + } 132 + 133 + switch valueType.Format { 134 + case db.ValueTypeFormatDid: 135 + id, err := v.resolver.ResolveIdent(context.Background(), labelOp.OperandValue) 136 + if err != nil { 137 + return fmt.Errorf("failed to resolve did/handle: %w", err) 138 + } 139 + 140 + labelOp.OperandValue = id.DID.String() 141 + 142 + case db.ValueTypeFormatAny, "": 143 + default: 144 + return fmt.Errorf("unsupported format constraint: %q", valueType.Format) 145 + } 146 + 147 + case db.ConcreteTypeInt: 148 + if labelOp.OperandValue == "" { 149 + return fmt.Errorf("integer type requires non-empty value") 150 + } 151 + if _, err := fmt.Sscanf(labelOp.OperandValue, "%d", new(int)); err != nil { 152 + return fmt.Errorf("value %q is not a valid integer", labelOp.OperandValue) 153 + } 154 + 155 + if valueType.IsEnumType() { 156 + if !slices.Contains(valueType.Enum, labelOp.OperandValue) { 157 + return fmt.Errorf("value %q is not in allowed enum values %v", labelOp.OperandValue, valueType.Enum) 158 + } 159 + } 160 + 161 + case db.ConcreteTypeBool: 162 + if labelOp.OperandValue != "true" && labelOp.OperandValue != "false" { 163 + return fmt.Errorf("boolean type requires value to be 'true' or 'false', got %q", labelOp.OperandValue) 164 + } 165 + 166 + // validate enum constraints if present (though uncommon for booleans) 167 + if valueType.IsEnumType() { 168 + if !slices.Contains(valueType.Enum, labelOp.OperandValue) { 169 + return fmt.Errorf("value %q is not in allowed enum values %v", labelOp.OperandValue, valueType.Enum) 170 + } 171 + } 172 + 173 + default: 174 + return fmt.Errorf("unsupported value type: %q", valueType.Type) 75 175 } 76 176 77 177 return nil
+4 -1
appview/validator/validator.go
··· 3 3 import ( 4 4 "tangled.org/core/appview/db" 5 5 "tangled.org/core/appview/pages/markup" 6 + "tangled.org/core/idresolver" 6 7 ) 7 8 8 9 type Validator struct { 9 10 db *db.DB 10 11 sanitizer markup.Sanitizer 12 + resolver *idresolver.Resolver 11 13 } 12 14 13 - func New(db *db.DB) *Validator { 15 + func New(db *db.DB, res *idresolver.Resolver) *Validator { 14 16 return &Validator{ 15 17 db: db, 16 18 sanitizer: markup.NewSanitizer(), 19 + resolver: res, 17 20 } 18 21 }