cmd/glot/breaking.go
cmd/glot/lex_breaking.go
cmd/glot/breaking.go
cmd/glot/lex_breaking.go
cmd/glot/check_dns.go
cmd/glot/lex_check_dns.go
cmd/glot/check_dns.go
cmd/glot/lex_check_dns.go
cmd/glot/codegen.go
cmd/glot/lex_codegen.go
cmd/glot/codegen.go
cmd/glot/lex_codegen.go
cmd/glot/diff.go
cmd/glot/lex_diff.go
cmd/glot/diff.go
cmd/glot/lex_diff.go
+163
cmd/glot/lex_util.go
+163
cmd/glot/lex_util.go
···
1
+
package main
2
+
3
+
import (
4
+
"context"
5
+
"encoding/json"
6
+
"fmt"
7
+
"log/slog"
8
+
"sort"
9
+
"strings"
10
+
11
+
"github.com/bluesky-social/indigo/api/agnostic"
12
+
"github.com/bluesky-social/indigo/atproto/atclient"
13
+
"github.com/bluesky-social/indigo/atproto/identity"
14
+
"github.com/bluesky-social/indigo/atproto/lexicon"
15
+
"github.com/bluesky-social/indigo/atproto/syntax"
16
+
17
+
"github.com/urfave/cli/v3"
18
+
)
19
+
20
+
var (
21
+
schemaNSID = syntax.NSID("com.atproto.lexicon.schema")
22
+
)
23
+
24
+
func nsidGroup(nsid syntax.NSID) string {
25
+
parts := strings.Split(string(nsid), ".")
26
+
g := strings.Join(parts[0:len(parts)-1], ".") + "."
27
+
return g
28
+
}
29
+
30
+
// Checks if a string is a valid NSID group pattern, which is a partial NSID ending in '.' or '.*'
31
+
func ParseNSIDGroup(raw string) (string, error) {
32
+
if strings.HasSuffix(raw, ".*") {
33
+
raw = raw[:len(raw)-1]
34
+
}
35
+
if !strings.HasSuffix(raw, ".") {
36
+
return "", fmt.Errorf("not an NSID group pattern")
37
+
}
38
+
_, err := syntax.ParseNSID(raw + "name")
39
+
if err != nil {
40
+
return "", fmt.Errorf("not an NSID group pattern")
41
+
}
42
+
return raw, nil
43
+
}
44
+
45
+
// helper which runs a comparison function across local and remote schemas, based on 'cmd' configuration
46
+
func runComparisons(ctx context.Context, cmd *cli.Command, comp func(ctx context.Context, cmd *cli.Command, nsid syntax.NSID, localJSON, remoteJSON json.RawMessage) error) error {
47
+
48
+
// collect all NSID/path mappings
49
+
localSchemas, err := collectSchemaJSON(cmd)
50
+
if err != nil {
51
+
return err
52
+
}
53
+
remoteSchemas := map[syntax.NSID]json.RawMessage{}
54
+
55
+
localGroups := map[string]bool{}
56
+
allNSIDMap := map[syntax.NSID]bool{}
57
+
for k := range localSchemas {
58
+
g := nsidGroup(k)
59
+
localGroups[g] = true
60
+
allNSIDMap[k] = true
61
+
}
62
+
63
+
for g := range localGroups {
64
+
if err := resolveLexiconGroup(ctx, cmd, g, &remoteSchemas); err != nil {
65
+
return err
66
+
}
67
+
}
68
+
69
+
for k := range remoteSchemas {
70
+
allNSIDMap[k] = true
71
+
}
72
+
allNSID := []string{}
73
+
for k := range allNSIDMap {
74
+
allNSID = append(allNSID, string(k))
75
+
}
76
+
sort.Strings(allNSID)
77
+
78
+
anyFailures := false
79
+
for _, k := range allNSID {
80
+
nsid := syntax.NSID(k)
81
+
if err := comp(ctx, cmd, nsid, localSchemas[nsid], remoteSchemas[nsid]); err != nil {
82
+
if err != ErrLintFailures {
83
+
return err
84
+
}
85
+
anyFailures = true
86
+
}
87
+
}
88
+
89
+
if anyFailures {
90
+
return ErrLintFailures
91
+
}
92
+
return nil
93
+
}
94
+
95
+
// helper which resolves and fetches all lexicon schemas (as JSON), storing them in provided map
96
+
func resolveLexiconGroup(ctx context.Context, cmd *cli.Command, group string, remote *map[syntax.NSID]json.RawMessage) error {
97
+
98
+
slog.Debug("resolving schemas for NSID group", "group", group)
99
+
100
+
// TODO: netclient support for listing records
101
+
dir := identity.BaseDirectory{}
102
+
did, err := dir.ResolveNSID(ctx, syntax.NSID(group+"name"))
103
+
if err != nil {
104
+
// if NSID isn't registered, just skip comparison
105
+
slog.Warn("skipping NSID pattern which did not resolve", "group", group)
106
+
return nil
107
+
}
108
+
ident, err := dir.LookupDID(ctx, did)
109
+
if err != nil {
110
+
return err
111
+
}
112
+
c := atclient.NewAPIClient(ident.PDSEndpoint())
113
+
114
+
cursor := ""
115
+
for {
116
+
// collection string, cursor string, limit int64, repo string, reverse bool
117
+
resp, err := agnostic.RepoListRecords(ctx, c, schemaNSID.String(), cursor, 100, ident.DID.String(), false)
118
+
if err != nil {
119
+
return err
120
+
}
121
+
for _, rec := range resp.Records {
122
+
aturi, err := syntax.ParseATURI(rec.Uri)
123
+
if err != nil {
124
+
return err
125
+
}
126
+
nsid, err := syntax.ParseNSID(aturi.RecordKey().String())
127
+
if err != nil {
128
+
slog.Warn("ignoring invalid schema NSID", "did", ident.DID, "rkey", aturi.RecordKey())
129
+
continue
130
+
}
131
+
if nsidGroup(nsid) != group {
132
+
// ignoring other NSIDs
133
+
continue
134
+
}
135
+
if rec.Value == nil {
136
+
return fmt.Errorf("missing record value: %s", nsid)
137
+
}
138
+
139
+
// parse file to check for errors
140
+
// TODO: use json/v2 when available for case-sensitivity
141
+
var sf lexicon.SchemaFile
142
+
err = json.Unmarshal(*rec.Value, &sf)
143
+
if err == nil {
144
+
err = sf.FinishParse()
145
+
}
146
+
if err == nil {
147
+
err = sf.CheckSchema()
148
+
}
149
+
if err != nil {
150
+
return fmt.Errorf("invalid lexicon schema record (%s): %w", nsid, err)
151
+
}
152
+
153
+
(*remote)[nsid] = *rec.Value
154
+
155
+
}
156
+
if resp.Cursor != nil && *resp.Cursor != "" {
157
+
cursor = *resp.Cursor
158
+
} else {
159
+
break
160
+
}
161
+
}
162
+
return nil
163
+
}
cmd/glot/lint.go
cmd/glot/lex_lint.go
cmd/glot/lint.go
cmd/glot/lex_lint.go
+4
-4
cmd/glot/new.go
cmd/glot/lex_new.go
+4
-4
cmd/glot/new.go
cmd/glot/lex_new.go
···
13
13
"github.com/urfave/cli/v3"
14
14
)
15
15
16
-
//go:embed templates/record.json
16
+
//go:embed lexicon-templates/record.json
17
17
var tmplRecord string
18
18
19
-
//go:embed templates/query-view.json
19
+
//go:embed lexicon-templates/query-view.json
20
20
var tmplQueryView string
21
21
22
-
//go:embed templates/query-list.json
22
+
//go:embed lexicon-templates/query-list.json
23
23
var tmplQueryList string
24
24
25
-
//go:embed templates/procedure.json
25
+
//go:embed lexicon-templates/procedure.json
26
26
var tmplProcedure string
27
27
28
28
var cmdNew = &cli.Command{
cmd/glot/publish.go
cmd/glot/lex_publish.go
cmd/glot/publish.go
cmd/glot/lex_publish.go
cmd/glot/pull.go
cmd/glot/lex_pull.go
cmd/glot/pull.go
cmd/glot/lex_pull.go
cmd/glot/status.go
cmd/glot/lex_status.go
cmd/glot/status.go
cmd/glot/lex_status.go
cmd/glot/templates/procedure.json
cmd/glot/lexicon-templates/procedure.json
cmd/glot/templates/procedure.json
cmd/glot/lexicon-templates/procedure.json
cmd/glot/templates/query-list.json
cmd/glot/lexicon-templates/query-list.json
cmd/glot/templates/query-list.json
cmd/glot/lexicon-templates/query-list.json
cmd/glot/templates/query-view.json
cmd/glot/lexicon-templates/query-view.json
cmd/glot/templates/query-view.json
cmd/glot/lexicon-templates/query-view.json
cmd/glot/templates/record.json
cmd/glot/lexicon-templates/record.json
cmd/glot/templates/record.json
cmd/glot/lexicon-templates/record.json
cmd/glot/unpublish.go
cmd/glot/lex_unpublish.go
cmd/glot/unpublish.go
cmd/glot/lex_unpublish.go
-33
cmd/glot/util.go
-33
cmd/glot/util.go
···
1
-
package main
2
-
3
-
import (
4
-
"fmt"
5
-
"strings"
6
-
7
-
"github.com/bluesky-social/indigo/atproto/syntax"
8
-
)
9
-
10
-
var (
11
-
schemaNSID = syntax.NSID("com.atproto.lexicon.schema")
12
-
)
13
-
14
-
func nsidGroup(nsid syntax.NSID) string {
15
-
parts := strings.Split(string(nsid), ".")
16
-
g := strings.Join(parts[0:len(parts)-1], ".") + "."
17
-
return g
18
-
}
19
-
20
-
// Checks if a string is a valid NSID group pattern, which is a partial NSID ending in '.' or '.*'
21
-
func ParseNSIDGroup(raw string) (string, error) {
22
-
if strings.HasSuffix(raw, ".*") {
23
-
raw = raw[:len(raw)-1]
24
-
}
25
-
if !strings.HasSuffix(raw, ".") {
26
-
return "", fmt.Errorf("not an NSID group pattern")
27
-
}
28
-
_, err := syntax.ParseNSID(raw + "name")
29
-
if err != nil {
30
-
return "", fmt.Errorf("not an NSID group pattern")
31
-
}
32
-
return raw, nil
33
-
}
-60
cmd/glot/util_compare.go
-60
cmd/glot/util_compare.go
···
1
-
package main
2
-
3
-
import (
4
-
"context"
5
-
"encoding/json"
6
-
"sort"
7
-
8
-
"github.com/bluesky-social/indigo/atproto/syntax"
9
-
10
-
"github.com/urfave/cli/v3"
11
-
)
12
-
13
-
func runComparisons(ctx context.Context, cmd *cli.Command, comp func(ctx context.Context, cmd *cli.Command, nsid syntax.NSID, localJSON, remoteJSON json.RawMessage) error) error {
14
-
15
-
// collect all NSID/path mappings
16
-
localSchemas, err := collectSchemaJSON(cmd)
17
-
if err != nil {
18
-
return err
19
-
}
20
-
remoteSchemas := map[syntax.NSID]json.RawMessage{}
21
-
22
-
localGroups := map[string]bool{}
23
-
allNSIDMap := map[syntax.NSID]bool{}
24
-
for k := range localSchemas {
25
-
g := nsidGroup(k)
26
-
localGroups[g] = true
27
-
allNSIDMap[k] = true
28
-
}
29
-
30
-
for g := range localGroups {
31
-
if err := resolveLexiconGroup(ctx, cmd, g, &remoteSchemas); err != nil {
32
-
return err
33
-
}
34
-
}
35
-
36
-
for k := range remoteSchemas {
37
-
allNSIDMap[k] = true
38
-
}
39
-
allNSID := []string{}
40
-
for k := range allNSIDMap {
41
-
allNSID = append(allNSID, string(k))
42
-
}
43
-
sort.Strings(allNSID)
44
-
45
-
anyFailures := false
46
-
for _, k := range allNSID {
47
-
nsid := syntax.NSID(k)
48
-
if err := comp(ctx, cmd, nsid, localSchemas[nsid], remoteSchemas[nsid]); err != nil {
49
-
if err != ErrLintFailures {
50
-
return err
51
-
}
52
-
anyFailures = true
53
-
}
54
-
}
55
-
56
-
if anyFailures {
57
-
return ErrLintFailures
58
-
}
59
-
return nil
60
-
}
-86
cmd/glot/util_fetch.go
-86
cmd/glot/util_fetch.go
···
1
-
package main
2
-
3
-
import (
4
-
"context"
5
-
"encoding/json"
6
-
"fmt"
7
-
"log/slog"
8
-
9
-
"github.com/bluesky-social/indigo/api/agnostic"
10
-
"github.com/bluesky-social/indigo/atproto/atclient"
11
-
"github.com/bluesky-social/indigo/atproto/identity"
12
-
"github.com/bluesky-social/indigo/atproto/lexicon"
13
-
"github.com/bluesky-social/indigo/atproto/syntax"
14
-
15
-
"github.com/urfave/cli/v3"
16
-
)
17
-
18
-
// helper which resolves and fetches all lexicon schemas (as JSON), storing them in provided map
19
-
func resolveLexiconGroup(ctx context.Context, cmd *cli.Command, group string, remote *map[syntax.NSID]json.RawMessage) error {
20
-
21
-
slog.Debug("resolving schemas for NSID group", "group", group)
22
-
23
-
// TODO: netclient support for listing records
24
-
dir := identity.BaseDirectory{}
25
-
did, err := dir.ResolveNSID(ctx, syntax.NSID(group+"name"))
26
-
if err != nil {
27
-
// if NSID isn't registered, just skip comparison
28
-
slog.Warn("skipping NSID pattern which did not resolve", "group", group)
29
-
return nil
30
-
}
31
-
ident, err := dir.LookupDID(ctx, did)
32
-
if err != nil {
33
-
return err
34
-
}
35
-
c := atclient.NewAPIClient(ident.PDSEndpoint())
36
-
37
-
cursor := ""
38
-
for {
39
-
// collection string, cursor string, limit int64, repo string, reverse bool
40
-
resp, err := agnostic.RepoListRecords(ctx, c, schemaNSID.String(), cursor, 100, ident.DID.String(), false)
41
-
if err != nil {
42
-
return err
43
-
}
44
-
for _, rec := range resp.Records {
45
-
aturi, err := syntax.ParseATURI(rec.Uri)
46
-
if err != nil {
47
-
return err
48
-
}
49
-
nsid, err := syntax.ParseNSID(aturi.RecordKey().String())
50
-
if err != nil {
51
-
slog.Warn("ignoring invalid schema NSID", "did", ident.DID, "rkey", aturi.RecordKey())
52
-
continue
53
-
}
54
-
if nsidGroup(nsid) != group {
55
-
// ignoring other NSIDs
56
-
continue
57
-
}
58
-
if rec.Value == nil {
59
-
return fmt.Errorf("missing record value: %s", nsid)
60
-
}
61
-
62
-
// parse file to check for errors
63
-
// TODO: use json/v2 when available for case-sensitivity
64
-
var sf lexicon.SchemaFile
65
-
err = json.Unmarshal(*rec.Value, &sf)
66
-
if err == nil {
67
-
err = sf.FinishParse()
68
-
}
69
-
if err == nil {
70
-
err = sf.CheckSchema()
71
-
}
72
-
if err != nil {
73
-
return fmt.Errorf("invalid lexicon schema record (%s): %w", nsid, err)
74
-
}
75
-
76
-
(*remote)[nsid] = *rec.Value
77
-
78
-
}
79
-
if resp.Cursor != nil && *resp.Cursor != "" {
80
-
cursor = *resp.Cursor
81
-
} else {
82
-
break
83
-
}
84
-
}
85
-
return nil
86
-
}
cmd/glot/util_files.go
cmd/glot/lex_util_files.go
cmd/glot/util_files.go
cmd/glot/lex_util_files.go