tangled
alpha
login
or
join now
back
round
1
view raw
appview/labels: add "subscribe all" button for default labels
#597
merged
opened by
oppi.li
3 months ago
targeting
master
from
push-lxxtrqtnnoxy
quickly subscribe to all default labels.
Signed-off-by: oppiliappan
me@oppi.li
options
unified
split
Changed files
+304
-42
appview
db
db.go
repos.go
ingester.go
labels
labels.go
models
label.go
pages
pages.go
templates
repo
settings
general.html
repo
repo.go
state
state.go
+1
-2
appview/db/db.go
···
527
-- label to subscribe to
528
label_at text not null,
529
530
-
unique (repo_at, label_at),
531
-
foreign key (label_at) references label_definitions (at_uri)
532
);
533
534
create table if not exists migrations (
···
527
-- label to subscribe to
528
label_at text not null,
529
530
+
unique (repo_at, label_at)
0
531
);
532
533
create table if not exists migrations (
+16
-3
appview/db/repos.go
···
345
return &repo, nil
346
}
347
348
-
func AddRepo(e Execer, repo *models.Repo) error {
349
-
_, err := e.Exec(
350
`insert into repos
351
(did, name, knot, rkey, at_uri, description, source)
352
values (?, ?, ?, ?, ?, ?, ?)`,
353
repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.RepoAt().String(), repo.Description, repo.Source,
354
)
355
-
return err
0
0
0
0
0
0
0
0
0
0
0
0
0
356
}
357
358
func RemoveRepo(e Execer, did, name string) error {
···
345
return &repo, nil
346
}
347
348
+
func AddRepo(tx *sql.Tx, repo *models.Repo) error {
349
+
_, err := tx.Exec(
350
`insert into repos
351
(did, name, knot, rkey, at_uri, description, source)
352
values (?, ?, ?, ?, ?, ?, ?)`,
353
repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.RepoAt().String(), repo.Description, repo.Source,
354
)
355
+
if err != nil {
356
+
return fmt.Errorf("failed to insert repo: %w", err)
357
+
}
358
+
359
+
for _, dl := range repo.Labels {
360
+
if err := SubscribeLabel(tx, &models.RepoLabel{
361
+
RepoAt: repo.RepoAt(),
362
+
LabelAt: syntax.ATURI(dl),
363
+
}); err != nil {
364
+
return fmt.Errorf("failed to subscribe to label: %w", err)
365
+
}
366
+
}
367
+
368
+
return nil
369
}
370
371
func RemoveRepo(e Execer, did, name string) error {
+80
appview/ingester.go
···
5
"encoding/json"
6
"fmt"
7
"log/slog"
0
0
8
9
"time"
10
···
80
err = i.ingestIssueComment(e)
81
case tangled.LabelDefinitionNSID:
82
err = i.ingestLabelDefinition(e)
0
0
83
}
84
l = i.Logger.With("nsid", e.Commit.Collection)
85
}
···
953
954
return nil
955
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
5
"encoding/json"
6
"fmt"
7
"log/slog"
8
+
"maps"
9
+
"slices"
10
11
"time"
12
···
82
err = i.ingestIssueComment(e)
83
case tangled.LabelDefinitionNSID:
84
err = i.ingestLabelDefinition(e)
85
+
case tangled.LabelOpNSID:
86
+
err = i.ingestLabelOp(e)
87
}
88
l = i.Logger.With("nsid", e.Commit.Collection)
89
}
···
957
958
return nil
959
}
960
+
961
+
func (i *Ingester) ingestLabelOp(e *jmodels.Event) error {
962
+
did := e.Did
963
+
rkey := e.Commit.RKey
964
+
965
+
var err error
966
+
967
+
l := i.Logger.With("handler", "ingestLabelOp", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
968
+
l.Info("ingesting record")
969
+
970
+
ddb, ok := i.Db.Execer.(*db.DB)
971
+
if !ok {
972
+
return fmt.Errorf("failed to index label op, invalid db cast")
973
+
}
974
+
975
+
switch e.Commit.Operation {
976
+
case jmodels.CommitOperationCreate:
977
+
raw := json.RawMessage(e.Commit.Record)
978
+
record := tangled.LabelOp{}
979
+
err = json.Unmarshal(raw, &record)
980
+
if err != nil {
981
+
return fmt.Errorf("invalid record: %w", err)
982
+
}
983
+
984
+
subject := syntax.ATURI(record.Subject)
985
+
collection := subject.Collection()
986
+
987
+
var repo *models.Repo
988
+
switch collection {
989
+
case tangled.RepoIssueNSID:
990
+
i, err := db.GetIssues(ddb, db.FilterEq("at_uri", subject))
991
+
if err != nil || len(i) != 1 {
992
+
return fmt.Errorf("failed to find subject: %w || subject count %d", err, len(i))
993
+
}
994
+
repo = i[0].Repo
995
+
default:
996
+
return fmt.Errorf("unsupport label subject: %s", collection)
997
+
}
998
+
999
+
actx, err := db.NewLabelApplicationCtx(ddb, db.FilterIn("at_uri", repo.Labels))
1000
+
if err != nil {
1001
+
return fmt.Errorf("failed to build label application ctx: %w", err)
1002
+
}
1003
+
1004
+
ops := models.LabelOpsFromRecord(did, rkey, record)
1005
+
1006
+
for _, o := range ops {
1007
+
def, ok := actx.Defs[o.OperandKey]
1008
+
if !ok {
1009
+
return fmt.Errorf("failed to find label def for key: %s, expected: %q", o.OperandKey, slices.Collect(maps.Keys(actx.Defs)))
1010
+
}
1011
+
if err := i.Validator.ValidateLabelOp(def, &o); err != nil {
1012
+
return fmt.Errorf("failed to validate labelop: %w", err)
1013
+
}
1014
+
}
1015
+
1016
+
tx, err := ddb.Begin()
1017
+
if err != nil {
1018
+
return err
1019
+
}
1020
+
defer tx.Rollback()
1021
+
1022
+
for _, o := range ops {
1023
+
_, err = db.AddLabelOp(tx, &o)
1024
+
if err != nil {
1025
+
return fmt.Errorf("failed to add labelop: %w", err)
1026
+
}
1027
+
}
1028
+
1029
+
if err = tx.Commit(); err != nil {
1030
+
return err
1031
+
}
1032
+
}
1033
+
1034
+
return nil
1035
+
}
-3
appview/labels/labels.go
···
104
return
105
}
106
107
-
l.logger.Info("actx", "labels", labelAts)
108
-
l.logger.Info("actx", "defs", actx.Defs)
109
-
110
// calculate the start state by applying already known labels
111
existingOps, err := db.GetLabelOps(l.db, db.FilterEq("subject", subjectUri))
112
if err != nil {
···
104
return
105
}
106
0
0
0
107
// calculate the start state by applying already known labels
108
existingOps, err := db.GetLabelOps(l.db, db.FilterEq("subject", subjectUri))
109
if err != nil {
+67
appview/models/label.go
···
1
package models
2
3
import (
0
4
"crypto/sha1"
5
"encoding/hex"
0
6
"errors"
7
"fmt"
8
"slices"
9
"time"
10
0
11
"github.com/bluesky-social/indigo/atproto/syntax"
0
12
"tangled.org/core/api/tangled"
13
"tangled.org/core/consts"
0
14
)
15
16
type ConcreteType string
···
471
472
return defs
473
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
package models
2
3
import (
4
+
"context"
5
"crypto/sha1"
6
"encoding/hex"
7
+
"encoding/json"
8
"errors"
9
"fmt"
10
"slices"
11
"time"
12
13
+
"github.com/bluesky-social/indigo/api/atproto"
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
21
type ConcreteType string
···
476
477
return defs
478
}
479
+
480
+
func FetchDefaultDefs(r *idresolver.Resolver) ([]LabelDefinition, error) {
481
+
resolved, err := r.ResolveIdent(context.Background(), consts.TangledDid)
482
+
if err != nil {
483
+
return nil, fmt.Errorf("failed to resolve tangled.sh DID %s: %v", consts.TangledDid, err)
484
+
}
485
+
pdsEndpoint := resolved.PDSEndpoint()
486
+
if pdsEndpoint == "" {
487
+
return nil, fmt.Errorf("no PDS endpoint found for tangled.sh DID %s", consts.TangledDid)
488
+
}
489
+
client := &xrpc.Client{
490
+
Host: pdsEndpoint,
491
+
}
492
+
493
+
var labelDefs []LabelDefinition
494
+
495
+
for _, dl := range DefaultLabelDefs() {
496
+
atUri := syntax.ATURI(dl)
497
+
parsedUri, err := syntax.ParseATURI(string(atUri))
498
+
if err != nil {
499
+
return nil, fmt.Errorf("failed to parse AT-URI %s: %v", atUri, err)
500
+
}
501
+
record, err := atproto.RepoGetRecord(
502
+
context.Background(),
503
+
client,
504
+
"",
505
+
parsedUri.Collection().String(),
506
+
parsedUri.Authority().String(),
507
+
parsedUri.RecordKey().String(),
508
+
)
509
+
if err != nil {
510
+
return nil, fmt.Errorf("failed to get record for %s: %v", atUri, err)
511
+
}
512
+
513
+
if record != nil {
514
+
bytes, err := record.Value.MarshalJSON()
515
+
if err != nil {
516
+
return nil, fmt.Errorf("failed to marshal record value for %s: %v", atUri, err)
517
+
}
518
+
519
+
raw := json.RawMessage(bytes)
520
+
labelRecord := tangled.LabelDefinition{}
521
+
err = json.Unmarshal(raw, &labelRecord)
522
+
if err != nil {
523
+
return nil, fmt.Errorf("invalid record for %s: %w", atUri, err)
524
+
}
525
+
526
+
labelDef, err := LabelDefinitionFromRecord(
527
+
parsedUri.Authority().String(),
528
+
parsedUri.RecordKey().String(),
529
+
labelRecord,
530
+
)
531
+
if err != nil {
532
+
return nil, fmt.Errorf("failed to create label definition from record %s: %v", atUri, err)
533
+
}
534
+
535
+
labelDefs = append(labelDefs, *labelDef)
536
+
}
537
+
}
538
+
539
+
return labelDefs, nil
540
+
}
+10
-9
appview/pages/pages.go
···
834
}
835
836
type RepoGeneralSettingsParams struct {
837
-
LoggedInUser *oauth.User
838
-
RepoInfo repoinfo.RepoInfo
839
-
Labels []models.LabelDefinition
840
-
DefaultLabels []models.LabelDefinition
841
-
SubscribedLabels map[string]struct{}
842
-
Active string
843
-
Tabs []map[string]any
844
-
Tab string
845
-
Branches []types.Branch
0
846
}
847
848
func (p *Pages) RepoGeneralSettings(w io.Writer, params RepoGeneralSettingsParams) error {
···
834
}
835
836
type RepoGeneralSettingsParams struct {
837
+
LoggedInUser *oauth.User
838
+
RepoInfo repoinfo.RepoInfo
839
+
Labels []models.LabelDefinition
840
+
DefaultLabels []models.LabelDefinition
841
+
SubscribedLabels map[string]struct{}
842
+
ShouldSubscribeAll bool
843
+
Active string
844
+
Tabs []map[string]any
845
+
Tab string
846
+
Branches []types.Branch
847
}
848
849
func (p *Pages) RepoGeneralSettings(w io.Writer, params RepoGeneralSettingsParams) error {
+36
-6
appview/pages/templates/repo/settings/general.html
···
46
47
{{ define "defaultLabelSettings" }}
48
<div class="flex flex-col gap-2">
49
-
<h2 class="text-sm pb-2 uppercase font-bold">Default Labels</h2>
50
-
<p class="text-gray-500 dark:text-gray-400">
51
-
Manage your issues and pulls by creating labels to categorize them. Only
52
-
repository owners may configure labels. You may choose to subscribe to
53
-
default labels, or create entirely custom labels.
54
-
</p>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
55
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
56
{{ range .DefaultLabels }}
57
<div id="label-{{.Id}}" class="flex items-center justify-between p-2 pl-4">
···
46
47
{{ define "defaultLabelSettings" }}
48
<div class="flex flex-col gap-2">
49
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
50
+
<div class="col-span-1 md:col-span-2">
51
+
<h2 class="text-sm pb-2 uppercase font-bold">Default Labels</h2>
52
+
<p class="text-gray-500 dark:text-gray-400">
53
+
Manage your issues and pulls by creating labels to categorize them. Only
54
+
repository owners may configure labels. You may choose to subscribe to
55
+
default labels, or create entirely custom labels.
56
+
<p>
57
+
</div>
58
+
<form class="col-span-1 md:col-span-1 md:justify-self-end">
59
+
{{ $title := "Unubscribe from all labels" }}
60
+
{{ $icon := "x" }}
61
+
{{ $text := "unsubscribe all" }}
62
+
{{ $action := "unsubscribe" }}
63
+
{{ if $.ShouldSubscribeAll }}
64
+
{{ $title = "Subscribe to all labels" }}
65
+
{{ $icon = "check-check" }}
66
+
{{ $text = "subscribe all" }}
67
+
{{ $action = "subscribe" }}
68
+
{{ end }}
69
+
{{ range .DefaultLabels }}
70
+
<input type="hidden" name="label" value="{{ .AtUri.String }}">
71
+
{{ end }}
72
+
<button
73
+
type="submit"
74
+
title="{{$title}}"
75
+
class="btn flex items-center gap-2 group"
76
+
hx-swap="none"
77
+
hx-post="/{{ $.RepoInfo.FullName }}/settings/label/{{$action}}"
78
+
{{ if not .RepoInfo.Roles.IsOwner }}disabled{{ end }}>
79
+
{{ i $icon "size-4" }}
80
+
{{ $text }}
81
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
82
+
</button>
83
+
</form>
84
+
</div>
85
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
86
{{ range .DefaultLabels }}
87
<div id="label-{{.Id}}" class="flex items-center justify-between p-2 pl-4">
+61
-19
appview/repo/repo.go
···
1248
return
1249
}
1250
0
0
0
0
0
1251
errorId := "default-label-operation"
1252
fail := func(msg string, err error) {
1253
l.Error(msg, "err", err)
1254
rp.pages.Notice(w, errorId, msg)
1255
}
1256
1257
-
labelAt := r.FormValue("label")
1258
-
_, err = db.GetLabelDefinition(rp.db, db.FilterEq("at_uri", labelAt))
1259
if err != nil {
1260
fail("Failed to subscribe to label.", err)
1261
return
1262
}
1263
1264
newRepo := f.Repo
1265
-
newRepo.Labels = append(newRepo.Labels, labelAt)
0
0
0
0
0
1266
repoRecord := newRepo.AsRecord()
1267
1268
client, err := rp.oauth.AuthorizedClient(r)
···
1286
},
1287
})
1288
1289
-
err = db.SubscribeLabel(rp.db, &models.RepoLabel{
1290
-
RepoAt: f.RepoAt(),
1291
-
LabelAt: syntax.ATURI(labelAt),
1292
-
})
1293
if err != nil {
1294
fail("Failed to subscribe to label.", err)
1295
return
1296
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1297
1298
// everything succeeded
1299
rp.pages.HxRefresh(w)
···
1311
return
1312
}
1313
0
0
0
0
0
1314
errorId := "default-label-operation"
1315
fail := func(msg string, err error) {
1316
l.Error(msg, "err", err)
1317
rp.pages.Notice(w, errorId, msg)
1318
}
1319
1320
-
labelAt := r.FormValue("label")
1321
-
_, err = db.GetLabelDefinition(rp.db, db.FilterEq("at_uri", labelAt))
1322
if err != nil {
1323
fail("Failed to unsubscribe to label.", err)
1324
return
···
1328
newRepo := f.Repo
1329
var updated []string
1330
for _, l := range newRepo.Labels {
1331
-
if l != labelAt {
1332
updated = append(updated, l)
1333
}
1334
}
···
1359
err = db.UnsubscribeLabel(
1360
rp.db,
1361
db.FilterEq("repo_at", f.RepoAt()),
1362
-
db.FilterEq("label_at", labelAt),
1363
)
1364
if err != nil {
1365
fail("Failed to unsubscribe label.", err)
···
1927
subscribedLabels[l] = struct{}{}
1928
}
1929
0
0
0
0
0
0
0
0
0
0
0
1930
rp.pages.RepoGeneralSettings(w, pages.RepoGeneralSettingsParams{
1931
-
LoggedInUser: user,
1932
-
RepoInfo: f.RepoInfo(user),
1933
-
Branches: result.Branches,
1934
-
Labels: labels,
1935
-
DefaultLabels: defaultLabels,
1936
-
SubscribedLabels: subscribedLabels,
1937
-
Tabs: settingsTabs,
1938
-
Tab: "general",
0
1939
})
1940
}
1941
···
2150
Source: sourceAt,
2151
Description: existingRepo.Description,
2152
Created: time.Now(),
0
2153
}
2154
record := repo.AsRecord()
2155
···
1248
return
1249
}
1250
1251
+
if err := r.ParseForm(); err != nil {
1252
+
l.Error("invalid form", "err", err)
1253
+
return
1254
+
}
1255
+
1256
errorId := "default-label-operation"
1257
fail := func(msg string, err error) {
1258
l.Error(msg, "err", err)
1259
rp.pages.Notice(w, errorId, msg)
1260
}
1261
1262
+
labelAts := r.Form["label"]
1263
+
_, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts))
1264
if err != nil {
1265
fail("Failed to subscribe to label.", err)
1266
return
1267
}
1268
1269
newRepo := f.Repo
1270
+
newRepo.Labels = append(newRepo.Labels, labelAts...)
1271
+
1272
+
// dedup
1273
+
slices.Sort(newRepo.Labels)
1274
+
newRepo.Labels = slices.Compact(newRepo.Labels)
1275
+
1276
repoRecord := newRepo.AsRecord()
1277
1278
client, err := rp.oauth.AuthorizedClient(r)
···
1296
},
1297
})
1298
1299
+
tx, err := rp.db.Begin()
0
0
0
1300
if err != nil {
1301
fail("Failed to subscribe to label.", err)
1302
return
1303
}
1304
+
defer tx.Rollback()
1305
+
1306
+
for _, l := range labelAts {
1307
+
err = db.SubscribeLabel(tx, &models.RepoLabel{
1308
+
RepoAt: f.RepoAt(),
1309
+
LabelAt: syntax.ATURI(l),
1310
+
})
1311
+
if err != nil {
1312
+
fail("Failed to subscribe to label.", err)
1313
+
return
1314
+
}
1315
+
}
1316
+
1317
+
if err := tx.Commit(); err != nil {
1318
+
fail("Failed to subscribe to label.", err)
1319
+
return
1320
+
}
1321
1322
// everything succeeded
1323
rp.pages.HxRefresh(w)
···
1335
return
1336
}
1337
1338
+
if err := r.ParseForm(); err != nil {
1339
+
l.Error("invalid form", "err", err)
1340
+
return
1341
+
}
1342
+
1343
errorId := "default-label-operation"
1344
fail := func(msg string, err error) {
1345
l.Error(msg, "err", err)
1346
rp.pages.Notice(w, errorId, msg)
1347
}
1348
1349
+
labelAts := r.Form["label"]
1350
+
_, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts))
1351
if err != nil {
1352
fail("Failed to unsubscribe to label.", err)
1353
return
···
1357
newRepo := f.Repo
1358
var updated []string
1359
for _, l := range newRepo.Labels {
1360
+
if !slices.Contains(labelAts, l) {
1361
updated = append(updated, l)
1362
}
1363
}
···
1388
err = db.UnsubscribeLabel(
1389
rp.db,
1390
db.FilterEq("repo_at", f.RepoAt()),
1391
+
db.FilterIn("label_at", labelAts),
1392
)
1393
if err != nil {
1394
fail("Failed to unsubscribe label.", err)
···
1956
subscribedLabels[l] = struct{}{}
1957
}
1958
1959
+
// if there is atleast 1 unsubbed default label, show the "subscribe all" button,
1960
+
// if all default labels are subbed, show the "unsubscribe all" button
1961
+
shouldSubscribeAll := false
1962
+
for _, dl := range defaultLabels {
1963
+
if _, ok := subscribedLabels[dl.AtUri().String()]; !ok {
1964
+
// one of the default labels is not subscribed to
1965
+
shouldSubscribeAll = true
1966
+
break
1967
+
}
1968
+
}
1969
+
1970
rp.pages.RepoGeneralSettings(w, pages.RepoGeneralSettingsParams{
1971
+
LoggedInUser: user,
1972
+
RepoInfo: f.RepoInfo(user),
1973
+
Branches: result.Branches,
1974
+
Labels: labels,
1975
+
DefaultLabels: defaultLabels,
1976
+
SubscribedLabels: subscribedLabels,
1977
+
ShouldSubscribeAll: shouldSubscribeAll,
1978
+
Tabs: settingsTabs,
1979
+
Tab: "general",
1980
})
1981
}
1982
···
2191
Source: sourceAt,
2192
Description: existingRepo.Description,
2193
Created: time.Now(),
2194
+
Labels: models.DefaultLabelDefs(),
2195
}
2196
record := repo.AsRecord()
2197
+33
appview/state/state.go
···
103
tangled.RepoIssueNSID,
104
tangled.RepoIssueCommentNSID,
105
tangled.LabelDefinitionNSID,
0
106
},
107
nil,
108
slog.Default(),
···
117
return nil, fmt.Errorf("failed to create jetstream client: %w", err)
118
}
119
0
0
0
0
120
ingester := appview.Ingester{
121
Db: wrapper,
122
Enforcer: enforcer,
···
440
Rkey: rkey,
441
Description: description,
442
Created: time.Now(),
0
443
}
444
record := repo.AsRecord()
445
···
580
})
581
return err
582
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
103
tangled.RepoIssueNSID,
104
tangled.RepoIssueCommentNSID,
105
tangled.LabelDefinitionNSID,
106
+
tangled.LabelOpNSID,
107
},
108
nil,
109
slog.Default(),
···
118
return nil, fmt.Errorf("failed to create jetstream client: %w", err)
119
}
120
121
+
if err := db.BackfillDefaultDefs(d, res); err != nil {
122
+
return nil, fmt.Errorf("failed to backfill default label defs: %w", err)
123
+
}
124
+
125
ingester := appview.Ingester{
126
Db: wrapper,
127
Enforcer: enforcer,
···
445
Rkey: rkey,
446
Description: description,
447
Created: time.Now(),
448
+
Labels: models.DefaultLabelDefs(),
449
}
450
record := repo.AsRecord()
451
···
586
})
587
return err
588
}
589
+
590
+
func BackfillDefaultDefs(e db.Execer, r *idresolver.Resolver) error {
591
+
defaults := models.DefaultLabelDefs()
592
+
defaultLabels, err := db.GetLabelDefinitions(e, db.FilterIn("at_uri", defaults))
593
+
if err != nil {
594
+
return err
595
+
}
596
+
// already present
597
+
if len(defaultLabels) == len(defaults) {
598
+
return nil
599
+
}
600
+
601
+
labelDefs, err := models.FetchDefaultDefs(r)
602
+
if err != nil {
603
+
return err
604
+
}
605
+
606
+
// Insert each label definition to the database
607
+
for _, labelDef := range labelDefs {
608
+
_, err = db.AddLabelDefinition(e, &labelDef)
609
+
if err != nil {
610
+
return fmt.Errorf("failed to add label definition %s: %v", labelDef.Name, err)
611
+
}
612
+
}
613
+
614
+
return nil
615
+
}