-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfind.go
More file actions
154 lines (146 loc) · 5.25 KB
/
find.go
File metadata and controls
154 lines (146 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package cli
import (
"fmt"
"path/filepath"
"github.com/randomcodespace/codeiq/internal/graph"
"github.com/spf13/cobra"
)
func init() {
registerSubcommand(newFindCommand)
}
// findKindSpec is one row of the finder-subcommand table: a sub-name, the
// NodeKind it filters on, plus the short / long doc strings.
type findKindSpec struct {
name, kind, short, long string
}
// findKindSpecs is the table of preset finders. Order is preserved in `--help`
// output; new finders go to the bottom.
var findKindSpecs = []findKindSpec{
{
"endpoints", "endpoint",
"List ENDPOINT nodes from the graph.",
`Return all REST / gRPC / messaging endpoint nodes from the enriched
graph, paginated. Endpoints are produced by detectors such as Spring REST,
Flask / FastAPI / Django routes, Express controllers, gRPC server stubs, and
the Kafka @KafkaListener family.`,
},
{
"guards", "guard",
"List GUARD nodes (auth filters, route guards) from the graph.",
`Return all GUARD nodes from the enriched graph. Guards represent auth
filters / route guards / middleware-style gatekeepers — Spring Security
filters, FastAPI Depends, Angular route guards, etc.`,
},
{
"entities", "entity",
"List ENTITY nodes (JPA / ORM entities) from the graph.",
`Return all persisted ENTITY nodes from the enriched graph. Entities
are produced by ORM detectors (JPA, EF Core, Django models, SQLAlchemy,
Sequelize, TypeORM, GORM, ...).`,
},
{
"topics", "topic",
"List TOPIC nodes (Kafka, RabbitMQ, Redis Streams, ...) from the graph.",
`Return all messaging TOPIC nodes from the enriched graph. Topics are
emitted by messaging detectors — Kafka @KafkaListener / @SendTo, Spring
Cloud Stream bindings, NestJS @MessagePattern, Rust lapin queues, etc.`,
},
{
"queues", "queue",
"List QUEUE nodes from the graph.",
`Return all messaging QUEUE nodes from the enriched graph. Queues are
detected separately from topics — JMS / SQS / RabbitMQ direct queues live
here, while pub-sub topics live under ` + "`find topics`" + `.`,
},
{
"services", "service",
"List SERVICE nodes (module/service boundaries) from the graph.",
`Return all SERVICE nodes from the enriched graph. SERVICE nodes are
synthesised by ServiceDetector from build files (pom.xml, package.json,
Cargo.toml, ...) and represent module / service boundaries.`,
},
{
"databases", "database_connection",
"List DATABASE_CONNECTION nodes from the graph.",
`Return all DATABASE_CONNECTION nodes from the enriched graph. These
are detected from JDBC URLs, application-yml datasource blocks, EF Core
DbContext configurations, Sequelize / TypeORM connection options, ...`,
},
{
"components", "component",
"List COMPONENT nodes (frontend components) from the graph.",
`Return all frontend COMPONENT nodes from the enriched graph —
React / Vue / Angular / Svelte component declarations detected by the
frontend extractor family.`,
},
}
// newFindCommand assembles the `find` parent and one finder subcommand per
// entry in findKindSpecs.
func newFindCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "find <what> [path]",
Short: "Preset finders for common node kinds (endpoints, guards, entities, ...).",
Long: `Preset finders return paginated lists of nodes of a given kind from
the enriched graph. Higher-level than ` + "`codeiq query`" + `, which operates on
individual node ids; ` + "`codeiq find`" + ` returns whole categories.
Each finder accepts ` + "`--limit`" + ` / ` + "`--offset`" + ` for paging and produces
tab-separated ` + "`id\\tlabel`" + ` rows ordered by id.`,
Example: ` codeiq find endpoints
codeiq find entities --limit 50
codeiq find services /repo --graph-dir /tmp/scratch.kuzu`,
RunE: func(c *cobra.Command, _ []string) error { return c.Help() },
}
for _, spec := range findKindSpecs {
cmd.AddCommand(newFindKindCommand(spec))
}
return cmd
}
// newFindKindCommand returns one finder subcommand for the given spec. The
// shared body resolves the path / graph-dir, opens the store, calls
// FindByKindPaginated, and prints `id\tlabel` rows.
func newFindKindCommand(spec findKindSpec) *cobra.Command {
var (
graphDir string
limit int
offset int
)
cmd := &cobra.Command{
Use: spec.name + " [path]",
Short: spec.short,
Long: spec.long,
Example: fmt.Sprintf(` codeiq find %s
codeiq find %s --limit 200
codeiq find %s /repo`, spec.name, spec.name, spec.name),
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
root, err := resolvePath(args)
if err != nil {
return err
}
gdir := graphDir
if gdir == "" {
gdir = filepath.Join(root, ".codeiq", "graph", "codeiq.kuzu")
}
store, err := graph.Open(gdir)
if err != nil {
return fmt.Errorf("open graph %s: %w", gdir, err)
}
defer store.Close()
nodes, err := store.FindByKindPaginated(spec.kind, offset, limit)
if err != nil {
return err
}
for _, n := range nodes {
fmt.Fprintf(cmd.OutOrStdout(), "%s\t%s\n", n.ID, n.Label)
}
return nil
},
}
cmd.Flags().StringVar(&graphDir, "graph-dir", "",
"Path to the Kuzu graph store (default: <path>/.codeiq/graph/codeiq.kuzu).")
cmd.Flags().IntVar(&limit, "limit", 100,
"Maximum number of rows to return (default: 100).")
cmd.Flags().IntVar(&offset, "offset", 0,
"Skip the first N rows (default: 0).")
return cmd
}