Skip to content

Commit 24b19c9

Browse files
yevgenypatshermanschaafdisqerezrokah
authored
feat!: Update to SDK V4 (#984)
Instead of this #915 (fix rebase conflicts) This introduce a single API for both destination and sources. Now plugins should be able to both read and write. This also goes together with a proto upgrade https://github.com/cloudquery/plugin-pb-go/tree/main/pb/plugin/v3 --------- Co-authored-by: Herman Schaaf <[email protected]> Co-authored-by: Kemal Hadimli <[email protected]> Co-authored-by: Erez Rokah <[email protected]>
1 parent 21e11bf commit 24b19c9

File tree

119 files changed

+4841
-5161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+4841
-5161
lines changed

.github/workflows/lint_markdown.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Vale
1717
uses: errata-ai/vale-action@v2
1818
with:
19-
vale_flags: "--glob=!{plugins/source/testdata/*,CHANGELOG.md,.github/styles/proselint/README.md}"
19+
vale_flags: "--glob=!{docs/testdata/*,CHANGELOG.md,.github/styles/proselint/README.md}"
2020
filter_mode: nofilter
2121
env:
2222
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
@@ -31,4 +31,4 @@ jobs:
3131
with:
3232
files: .
3333
config_file: .markdownlint.yaml
34-
ignore_files: "{plugins/source/testdata/*,CHANGELOG.md}"
34+
ignore_files: "{docs/testdata/*,CHANGELOG.md}"

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ config.hcl
88
vendor
99
cover.out
1010
.delta.*
11-
bench.json
11+
bench.json
12+
serve/^TestPluginDocs$/
13+
cover

backend/backend.go

Lines changed: 0 additions & 12 deletions
This file was deleted.

buf.yaml

Lines changed: 0 additions & 12 deletions
This file was deleted.

docs/generator.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package docs
2+
3+
import (
4+
"embed"
5+
"fmt"
6+
"os"
7+
"regexp"
8+
"sort"
9+
10+
"github.com/cloudquery/plugin-sdk/v4/caser"
11+
"github.com/cloudquery/plugin-sdk/v4/schema"
12+
)
13+
14+
//go:embed templates/*.go.tpl
15+
var templatesFS embed.FS
16+
17+
var reMatchNewlines = regexp.MustCompile(`\n{3,}`)
18+
var reMatchHeaders = regexp.MustCompile(`(#{1,6}.+)\n+`)
19+
20+
var DefaultTitleExceptions = map[string]string{
21+
// common abbreviations
22+
"acl": "ACL",
23+
"acls": "ACLs",
24+
"api": "API",
25+
"apis": "APIs",
26+
"ca": "CA",
27+
"cidr": "CIDR",
28+
"cidrs": "CIDRs",
29+
"db": "DB",
30+
"dbs": "DBs",
31+
"dhcp": "DHCP",
32+
"iam": "IAM",
33+
"iot": "IOT",
34+
"ip": "IP",
35+
"ips": "IPs",
36+
"ipv4": "IPv4",
37+
"ipv6": "IPv6",
38+
"mfa": "MFA",
39+
"ml": "ML",
40+
"oauth": "OAuth",
41+
"vpc": "VPC",
42+
"vpcs": "VPCs",
43+
"vpn": "VPN",
44+
"vpns": "VPNs",
45+
"waf": "WAF",
46+
"wafs": "WAFs",
47+
48+
// cloud providers
49+
"aws": "AWS",
50+
"gcp": "GCP",
51+
}
52+
53+
type Format int
54+
55+
const (
56+
FormatMarkdown Format = iota
57+
FormatJSON
58+
)
59+
60+
func (r Format) String() string {
61+
return [...]string{"markdown", "json"}[r]
62+
}
63+
64+
func FormatFromString(s string) (Format, error) {
65+
switch s {
66+
case "markdown":
67+
return FormatMarkdown, nil
68+
case "json":
69+
return FormatJSON, nil
70+
default:
71+
return FormatMarkdown, fmt.Errorf("unknown format %s", s)
72+
}
73+
}
74+
75+
type Generator struct {
76+
tables schema.Tables
77+
titleTransformer func(*schema.Table) string
78+
pluginName string
79+
}
80+
81+
func DefaultTitleTransformer(table *schema.Table) string {
82+
if table.Title != "" {
83+
return table.Title
84+
}
85+
csr := caser.New(caser.WithCustomExceptions(DefaultTitleExceptions))
86+
return csr.ToTitle(table.Name)
87+
}
88+
89+
func sortTables(tables schema.Tables) {
90+
sort.SliceStable(tables, func(i, j int) bool {
91+
return tables[i].Name < tables[j].Name
92+
})
93+
94+
for _, table := range tables {
95+
sortTables(table.Relations)
96+
}
97+
}
98+
99+
// NewGenerator creates a new generator for the given tables.
100+
// The tables are sorted by name. pluginName is optional and is used in markdown only
101+
func NewGenerator(pluginName string, tables schema.Tables) *Generator {
102+
sortedTables := make(schema.Tables, 0, len(tables))
103+
for _, t := range tables {
104+
sortedTables = append(sortedTables, t.Copy(nil))
105+
}
106+
sortTables(sortedTables)
107+
108+
return &Generator{
109+
tables: sortedTables,
110+
titleTransformer: DefaultTitleTransformer,
111+
pluginName: pluginName,
112+
}
113+
}
114+
115+
func (g *Generator) Generate(dir string, format Format) error {
116+
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
117+
return err
118+
}
119+
120+
switch format {
121+
case FormatMarkdown:
122+
return g.renderTablesAsMarkdown(dir)
123+
case FormatJSON:
124+
return g.renderTablesAsJSON(dir)
125+
default:
126+
return fmt.Errorf("unsupported format: %v", format)
127+
}
128+
}
129+
130+
// setDestinationManagedCqColumns overwrites or adds the CQ columns that are managed by the destination plugins (_cq_sync_time, _cq_source_name).
131+
// func setDestinationManagedCqColumns(tables []*schema.Table) {
132+
// for _, table := range tables {
133+
// table.OverwriteOrAddColumn(&schema.CqSyncTimeColumn)
134+
// table.OverwriteOrAddColumn(&schema.CqSourceNameColumn)
135+
// setDestinationManagedCqColumns(table.Relations)
136+
// }
137+
// }

plugins/source/docs_test.go renamed to docs/generator_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//go:build !windows
22

3-
package source
3+
package docs
44

55
import (
66
"os"
@@ -9,8 +9,8 @@ import (
99

1010
"github.com/apache/arrow/go/v13/arrow"
1111
"github.com/bradleyjkemp/cupaloy/v2"
12-
"github.com/cloudquery/plugin-sdk/v3/schema"
13-
"github.com/cloudquery/plugin-sdk/v3/types"
12+
"github.com/cloudquery/plugin-sdk/v4/schema"
13+
"github.com/cloudquery/plugin-sdk/v4/types"
1414
"github.com/stretchr/testify/require"
1515
)
1616

@@ -120,14 +120,13 @@ var testTables = []*schema.Table{
120120
}
121121

122122
func TestGeneratePluginDocs(t *testing.T) {
123-
p := NewPlugin("test", "v1.0.0", testTables, newTestExecutionClient)
124-
123+
g := NewGenerator("test", testTables)
125124
cup := cupaloy.New(cupaloy.SnapshotSubdirectory("testdata"))
126125

127126
t.Run("Markdown", func(t *testing.T) {
128127
tmpdir := t.TempDir()
129128

130-
err := p.GeneratePluginDocs(tmpdir, "markdown")
129+
err := g.Generate(tmpdir, FormatMarkdown)
131130
if err != nil {
132131
t.Fatalf("unexpected error calling GeneratePluginDocs: %v", err)
133132
}
@@ -146,7 +145,7 @@ func TestGeneratePluginDocs(t *testing.T) {
146145
t.Run("JSON", func(t *testing.T) {
147146
tmpdir := t.TempDir()
148147

149-
err := p.GeneratePluginDocs(tmpdir, "json")
148+
err := g.Generate(tmpdir, FormatJSON)
150149
if err != nil {
151150
t.Fatalf("unexpected error calling GeneratePluginDocs: %v", err)
152151
}

docs/json.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package docs
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/cloudquery/plugin-sdk/v4/schema"
10+
)
11+
12+
type jsonTable struct {
13+
Name string `json:"name"`
14+
Title string `json:"title"`
15+
Description string `json:"description"`
16+
Columns []jsonColumn `json:"columns"`
17+
Relations []jsonTable `json:"relations"`
18+
}
19+
20+
type jsonColumn struct {
21+
Name string `json:"name"`
22+
Type string `json:"type"`
23+
IsPrimaryKey bool `json:"is_primary_key,omitempty"`
24+
IsIncrementalKey bool `json:"is_incremental_key,omitempty"`
25+
}
26+
27+
func (g *Generator) renderTablesAsJSON(dir string) error {
28+
jsonTables := g.jsonifyTables(g.tables)
29+
buffer := &bytes.Buffer{}
30+
m := json.NewEncoder(buffer)
31+
m.SetIndent("", " ")
32+
m.SetEscapeHTML(false)
33+
err := m.Encode(jsonTables)
34+
if err != nil {
35+
return err
36+
}
37+
outputPath := filepath.Join(dir, "__tables.json")
38+
return os.WriteFile(outputPath, buffer.Bytes(), 0644)
39+
}
40+
41+
func (g *Generator) jsonifyTables(tables schema.Tables) []jsonTable {
42+
jsonTables := make([]jsonTable, len(tables))
43+
for i, table := range tables {
44+
jsonColumns := make([]jsonColumn, len(table.Columns))
45+
for c, col := range table.Columns {
46+
jsonColumns[c] = jsonColumn{
47+
Name: col.Name,
48+
Type: col.Type.String(),
49+
IsPrimaryKey: col.PrimaryKey,
50+
IsIncrementalKey: col.IncrementalKey,
51+
}
52+
}
53+
jsonTables[i] = jsonTable{
54+
Name: table.Name,
55+
Title: g.titleTransformer(table),
56+
Description: table.Description,
57+
Columns: jsonColumns,
58+
Relations: g.jsonifyTables(table.Relations),
59+
}
60+
}
61+
return jsonTables
62+
}

docs/markdown.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package docs
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"text/template"
9+
10+
"github.com/cloudquery/plugin-sdk/v4/schema"
11+
)
12+
13+
type templateData struct {
14+
PluginName string
15+
Tables schema.Tables
16+
}
17+
18+
func (g *Generator) renderTablesAsMarkdown(dir string) error {
19+
for _, table := range g.tables {
20+
if err := g.renderAllTables(dir, table); err != nil {
21+
return err
22+
}
23+
}
24+
t, err := template.New("all_tables.md.go.tpl").Funcs(template.FuncMap{
25+
"indentToDepth": indentToDepth,
26+
}).ParseFS(templatesFS, "templates/all_tables*.md.go.tpl")
27+
if err != nil {
28+
return fmt.Errorf("failed to parse template for README.md: %v", err)
29+
}
30+
31+
var b bytes.Buffer
32+
if err := t.Execute(&b, templateData{PluginName: g.pluginName, Tables: g.tables}); err != nil {
33+
return fmt.Errorf("failed to execute template: %v", err)
34+
}
35+
content := formatMarkdown(b.String())
36+
outputPath := filepath.Join(dir, "README.md")
37+
f, err := os.Create(outputPath)
38+
if err != nil {
39+
return fmt.Errorf("failed to create file %v: %v", outputPath, err)
40+
}
41+
f.WriteString(content)
42+
return nil
43+
}
44+
45+
func (g *Generator) renderAllTables(dir string, t *schema.Table) error {
46+
if err := g.renderTable(dir, t); err != nil {
47+
return err
48+
}
49+
for _, r := range t.Relations {
50+
if err := g.renderAllTables(dir, r); err != nil {
51+
return err
52+
}
53+
}
54+
return nil
55+
}
56+
57+
func (g *Generator) renderTable(dir string, table *schema.Table) error {
58+
t := template.New("").Funcs(map[string]any{
59+
"title": g.titleTransformer,
60+
})
61+
t, err := t.New("table.md.go.tpl").ParseFS(templatesFS, "templates/table.md.go.tpl")
62+
if err != nil {
63+
return fmt.Errorf("failed to parse template: %v", err)
64+
}
65+
66+
outputPath := filepath.Join(dir, fmt.Sprintf("%s.md", table.Name))
67+
68+
var b bytes.Buffer
69+
if err := t.Execute(&b, table); err != nil {
70+
return fmt.Errorf("failed to execute template: %v", err)
71+
}
72+
content := formatMarkdown(b.String())
73+
f, err := os.Create(outputPath)
74+
if err != nil {
75+
return fmt.Errorf("failed to create file %v: %v", outputPath, err)
76+
}
77+
f.WriteString(content)
78+
return f.Close()
79+
}
80+
81+
func formatMarkdown(s string) string {
82+
s = reMatchNewlines.ReplaceAllString(s, "\n\n")
83+
return reMatchHeaders.ReplaceAllString(s, `$1`+"\n\n")
84+
}
85+
86+
func indentToDepth(table *schema.Table) string {
87+
s := ""
88+
t := table
89+
for t.Parent != nil {
90+
s += " "
91+
t = t.Parent
92+
}
93+
return s
94+
}

0 commit comments

Comments
 (0)