@datachonk/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/dist/__tests__/cache.test.d.ts +2 -0
- package/dist/__tests__/cache.test.d.ts.map +1 -0
- package/dist/__tests__/cache.test.js +40 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +113 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/templates.test.d.ts +2 -0
- package/dist/__tests__/templates.test.d.ts.map +1 -0
- package/dist/__tests__/templates.test.js +51 -0
- package/dist/__tests__/templates.test.js.map +1 -0
- package/dist/commands/analyze.d.ts +10 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +156 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +121 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +287 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/docs.d.ts +7 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +208 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/generate.d.ts +9 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +130 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +149 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lineage.d.ts +9 -0
- package/dist/commands/lineage.d.ts.map +1 -0
- package/dist/commands/lineage.js +186 -0
- package/dist/commands/lineage.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +213 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/review.d.ts +8 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +215 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/scan.d.ts +18 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +335 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +244 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/analyzer.d.ts +39 -0
- package/dist/utils/analyzer.d.ts.map +1 -0
- package/dist/utils/analyzer.js +310 -0
- package/dist/utils/analyzer.js.map +1 -0
- package/dist/utils/cache.d.ts +98 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +253 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +56 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +108 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/generators.d.ts +8 -0
- package/dist/utils/generators.d.ts.map +1 -0
- package/dist/utils/generators.js +265 -0
- package/dist/utils/generators.js.map +1 -0
- package/dist/utils/plugins.d.ts +94 -0
- package/dist/utils/plugins.d.ts.map +1 -0
- package/dist/utils/plugins.js +164 -0
- package/dist/utils/plugins.js.map +1 -0
- package/dist/utils/templates.d.ts +57 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +458 -0
- package/dist/utils/templates.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @datachonk/cli
|
|
2
|
+
|
|
3
|
+
AI-powered dbt expert CLI - analyze, generate, and optimize your dbt projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @datachonk/cli
|
|
9
|
+
# or
|
|
10
|
+
npx @datachonk/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Initialize DataChonk in your dbt project
|
|
17
|
+
cd your-dbt-project
|
|
18
|
+
datachonk init
|
|
19
|
+
|
|
20
|
+
# Analyze your project for issues
|
|
21
|
+
datachonk analyze
|
|
22
|
+
|
|
23
|
+
# Generate a new staging model
|
|
24
|
+
datachonk generate staging --name customers --source raw.stripe.customers
|
|
25
|
+
|
|
26
|
+
# Review code before committing
|
|
27
|
+
datachonk review
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
### `datachonk init`
|
|
33
|
+
|
|
34
|
+
Initialize DataChonk in your dbt project. Creates a `.datachonk.yml` configuration file.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
datachonk init
|
|
38
|
+
datachonk init --warehouse snowflake
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `datachonk analyze`
|
|
42
|
+
|
|
43
|
+
Analyze your dbt project for anti-patterns, issues, and optimization opportunities.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
datachonk analyze # Analyze entire project
|
|
47
|
+
datachonk analyze --model stg_users # Analyze specific model
|
|
48
|
+
datachonk analyze --fix # Auto-fix issues where possible
|
|
49
|
+
datachonk analyze --json # Output as JSON
|
|
50
|
+
datachonk analyze --verbose # Show all details
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `datachonk generate`
|
|
54
|
+
|
|
55
|
+
Generate dbt models, tests, and documentation.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
datachonk generate staging --name customers --source raw.stripe.customers
|
|
59
|
+
datachonk generate intermediate --name customer_orders --source stg_orders
|
|
60
|
+
datachonk generate mart --name customer_360 --source int_customer_orders
|
|
61
|
+
datachonk generate dim --name customers --source int_customers
|
|
62
|
+
datachonk generate fct --name orders --source int_orders
|
|
63
|
+
datachonk generate snapshot --name customers --source raw.crm.customers
|
|
64
|
+
datachonk generate source --name stripe --source raw.stripe
|
|
65
|
+
datachonk generate test --name validate_orders --source fct_orders
|
|
66
|
+
datachonk generate docs --name fct_orders
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
- `--name, -n`: Name for the generated model
|
|
71
|
+
- `--source, -s`: Source table or model
|
|
72
|
+
- `--output, -o`: Custom output path
|
|
73
|
+
- `--dry-run`: Preview without writing files
|
|
74
|
+
|
|
75
|
+
### `datachonk review`
|
|
76
|
+
|
|
77
|
+
Get AI-powered code review of your dbt models.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
datachonk review # Review staged git changes
|
|
81
|
+
datachonk review models/staging/*.sql # Review specific files
|
|
82
|
+
datachonk review --strict # Enable strict mode
|
|
83
|
+
datachonk review --json # Output as JSON
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `datachonk lineage`
|
|
87
|
+
|
|
88
|
+
Analyze and visualize model lineage.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
datachonk lineage # Show project overview
|
|
92
|
+
datachonk lineage fct_orders # Show lineage for specific model
|
|
93
|
+
datachonk lineage fct_orders --upstream # Show only upstream
|
|
94
|
+
datachonk lineage fct_orders --downstream # Show only downstream
|
|
95
|
+
datachonk lineage fct_orders --depth 5 # Limit depth
|
|
96
|
+
datachonk lineage --json # Output as JSON
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `datachonk docs`
|
|
100
|
+
|
|
101
|
+
Generate or enhance dbt documentation.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
datachonk docs # Document all models
|
|
105
|
+
datachonk docs stg_customers # Document specific model
|
|
106
|
+
datachonk docs --missing-only # Only document undocumented models
|
|
107
|
+
datachonk docs --enhance # Use AI to improve descriptions
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `datachonk config`
|
|
111
|
+
|
|
112
|
+
Manage DataChonk configuration.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
datachonk config list # Show all configuration
|
|
116
|
+
datachonk config get warehouse # Get specific value
|
|
117
|
+
datachonk config set warehouse bigquery # Set value
|
|
118
|
+
datachonk config reset # Reset to defaults
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
DataChonk stores configuration in `.datachonk.yml`:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
version: 1
|
|
127
|
+
warehouse: snowflake
|
|
128
|
+
modeling:
|
|
129
|
+
approach: kimball
|
|
130
|
+
conventions:
|
|
131
|
+
- stg_prefix
|
|
132
|
+
- int_prefix
|
|
133
|
+
- fct_prefix
|
|
134
|
+
- dim_prefix
|
|
135
|
+
- snake_case
|
|
136
|
+
ai:
|
|
137
|
+
enabled: true
|
|
138
|
+
apiKey: your-api-key
|
|
139
|
+
analysis:
|
|
140
|
+
ignorePaths:
|
|
141
|
+
- target/**
|
|
142
|
+
- dbt_packages/**
|
|
143
|
+
ignoreRules: []
|
|
144
|
+
generation:
|
|
145
|
+
defaultMaterialization: view
|
|
146
|
+
addDescriptions: true
|
|
147
|
+
addTests: true
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## CI/CD Integration
|
|
151
|
+
|
|
152
|
+
### GitHub Actions
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
name: dbt CI
|
|
156
|
+
|
|
157
|
+
on: [pull_request]
|
|
158
|
+
|
|
159
|
+
jobs:
|
|
160
|
+
lint:
|
|
161
|
+
runs-on: ubuntu-latest
|
|
162
|
+
steps:
|
|
163
|
+
- uses: actions/checkout@v3
|
|
164
|
+
- uses: actions/setup-node@v3
|
|
165
|
+
with:
|
|
166
|
+
node-version: '18'
|
|
167
|
+
- run: npm install -g datachonk
|
|
168
|
+
- run: datachonk analyze --json > analysis.json
|
|
169
|
+
- run: datachonk review --strict
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Pre-commit Hook
|
|
173
|
+
|
|
174
|
+
Add to `.pre-commit-config.yaml`:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
repos:
|
|
178
|
+
- repo: local
|
|
179
|
+
hooks:
|
|
180
|
+
- id: datachonk-lint
|
|
181
|
+
name: DataChonk Lint
|
|
182
|
+
entry: datachonk analyze
|
|
183
|
+
language: system
|
|
184
|
+
types: [sql]
|
|
185
|
+
pass_filenames: false
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Exit Codes
|
|
189
|
+
|
|
190
|
+
- `0`: Success / No critical issues
|
|
191
|
+
- `1`: Critical issues found / Command failed
|
|
192
|
+
|
|
193
|
+
## Environment Variables
|
|
194
|
+
|
|
195
|
+
- `DATACHONK_API_KEY`: API key for AI features (alternative to config file)
|
|
196
|
+
- `DATACHONK_API_URL`: Custom API URL (default: https://datachonk.dev)
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
// Mock the cache module with test directory
|
|
5
|
+
const TEST_CACHE_DIR = join(process.cwd(), ".test-cache");
|
|
6
|
+
describe("Cache Utils", () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
if (!existsSync(TEST_CACHE_DIR)) {
|
|
9
|
+
mkdirSync(TEST_CACHE_DIR, { recursive: true });
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
if (existsSync(TEST_CACHE_DIR)) {
|
|
14
|
+
rmSync(TEST_CACHE_DIR, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
describe("cache operations", () => {
|
|
18
|
+
it("should handle cache directory creation", () => {
|
|
19
|
+
expect(existsSync(TEST_CACHE_DIR)).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("should handle missing cache gracefully", () => {
|
|
22
|
+
// Cache operations should not throw when cache doesn't exist
|
|
23
|
+
rmSync(TEST_CACHE_DIR, { recursive: true, force: true });
|
|
24
|
+
expect(existsSync(TEST_CACHE_DIR)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("cache key generation", () => {
|
|
28
|
+
it("generates consistent keys for same input", () => {
|
|
29
|
+
const input1 = JSON.stringify({ query: "test", version: 1 });
|
|
30
|
+
const input2 = JSON.stringify({ query: "test", version: 1 });
|
|
31
|
+
expect(input1).toBe(input2);
|
|
32
|
+
});
|
|
33
|
+
it("generates different keys for different input", () => {
|
|
34
|
+
const input1 = JSON.stringify({ query: "test1" });
|
|
35
|
+
const input2 = JSON.stringify({ query: "test2" });
|
|
36
|
+
expect(input1).not.toBe(input2);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../src/__tests__/cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,4CAA4C;AAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;AAE1D,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAChC,SAAS,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,6DAA6D;YAC7D,MAAM,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { loadConfig, saveConfig, getApiUrl, DEFAULT_API_URL, } from "../utils/config.js";
|
|
5
|
+
const TEST_DIR = join(process.cwd(), ".test-config");
|
|
6
|
+
const CONFIG_PATH = join(TEST_DIR, ".datachonk.yml");
|
|
7
|
+
describe("Config Utils", () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Create test directory
|
|
10
|
+
if (!existsSync(TEST_DIR)) {
|
|
11
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
// Clear env vars
|
|
14
|
+
delete process.env.DATACHONK_API_URL;
|
|
15
|
+
delete process.env.DATACHONK_API_KEY;
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
// Clean up test directory
|
|
19
|
+
if (existsSync(TEST_DIR)) {
|
|
20
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
describe("loadConfig", () => {
|
|
24
|
+
it("returns default config when no file exists", () => {
|
|
25
|
+
const config = loadConfig(TEST_DIR);
|
|
26
|
+
expect(config.version).toBe(1);
|
|
27
|
+
expect(config.warehouse).toBe("snowflake");
|
|
28
|
+
expect(config.modeling.approach).toBe("kimball");
|
|
29
|
+
});
|
|
30
|
+
it("loads config from YAML file", () => {
|
|
31
|
+
const yamlContent = `
|
|
32
|
+
version: 1
|
|
33
|
+
warehouse: bigquery
|
|
34
|
+
modeling:
|
|
35
|
+
approach: data_vault
|
|
36
|
+
conventions:
|
|
37
|
+
- snake_case
|
|
38
|
+
`;
|
|
39
|
+
writeFileSync(CONFIG_PATH, yamlContent);
|
|
40
|
+
const config = loadConfig(TEST_DIR);
|
|
41
|
+
expect(config.warehouse).toBe("bigquery");
|
|
42
|
+
expect(config.modeling.approach).toBe("data_vault");
|
|
43
|
+
});
|
|
44
|
+
it("merges with defaults for missing fields", () => {
|
|
45
|
+
const yamlContent = `
|
|
46
|
+
version: 1
|
|
47
|
+
warehouse: redshift
|
|
48
|
+
`;
|
|
49
|
+
writeFileSync(CONFIG_PATH, yamlContent);
|
|
50
|
+
const config = loadConfig(TEST_DIR);
|
|
51
|
+
expect(config.warehouse).toBe("redshift");
|
|
52
|
+
expect(config.modeling.approach).toBe("kimball"); // default
|
|
53
|
+
expect(config.ai.enabled).toBe(false); // default
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("saveConfig", () => {
|
|
57
|
+
it("saves config to YAML file", () => {
|
|
58
|
+
const config = {
|
|
59
|
+
version: 1,
|
|
60
|
+
warehouse: "databricks",
|
|
61
|
+
modeling: {
|
|
62
|
+
approach: "obt",
|
|
63
|
+
conventions: ["snake_case"],
|
|
64
|
+
},
|
|
65
|
+
ai: {
|
|
66
|
+
enabled: true,
|
|
67
|
+
apiKey: "test-key",
|
|
68
|
+
},
|
|
69
|
+
analysis: {
|
|
70
|
+
ignorePaths: [],
|
|
71
|
+
ignoreRules: [],
|
|
72
|
+
},
|
|
73
|
+
generation: {
|
|
74
|
+
defaultMaterialization: "table",
|
|
75
|
+
addDescriptions: true,
|
|
76
|
+
addTests: true,
|
|
77
|
+
},
|
|
78
|
+
connections: {},
|
|
79
|
+
};
|
|
80
|
+
saveConfig(config, TEST_DIR);
|
|
81
|
+
expect(existsSync(CONFIG_PATH)).toBe(true);
|
|
82
|
+
const loaded = loadConfig(TEST_DIR);
|
|
83
|
+
expect(loaded.warehouse).toBe("databricks");
|
|
84
|
+
expect(loaded.modeling.approach).toBe("obt");
|
|
85
|
+
expect(loaded.ai.enabled).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("getApiUrl", () => {
|
|
89
|
+
it("returns default URL when no config or env var", () => {
|
|
90
|
+
const url = getApiUrl();
|
|
91
|
+
expect(url).toBe(DEFAULT_API_URL);
|
|
92
|
+
});
|
|
93
|
+
it("respects DATACHONK_API_URL env var", () => {
|
|
94
|
+
process.env.DATACHONK_API_URL = "https://custom.example.com";
|
|
95
|
+
const url = getApiUrl();
|
|
96
|
+
expect(url).toBe("https://custom.example.com");
|
|
97
|
+
});
|
|
98
|
+
it("config apiUrl takes precedence", () => {
|
|
99
|
+
const yamlContent = `
|
|
100
|
+
version: 1
|
|
101
|
+
apiUrl: https://config.example.com
|
|
102
|
+
`;
|
|
103
|
+
writeFileSync(CONFIG_PATH, yamlContent);
|
|
104
|
+
process.env.DATACHONK_API_URL = "https://env.example.com";
|
|
105
|
+
// Note: This test would need the config to be loaded from TEST_DIR
|
|
106
|
+
// which requires modifying getApiUrl to accept a path parameter
|
|
107
|
+
// For now, just verify the env var works
|
|
108
|
+
const url = getApiUrl();
|
|
109
|
+
expect(url).toBe("https://env.example.com");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { initTemplates, getTemplate, listTemplates, renderTemplate, } from "../utils/templates.js";
|
|
3
|
+
describe("Template Utils", () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
initTemplates();
|
|
6
|
+
});
|
|
7
|
+
describe("listTemplates", () => {
|
|
8
|
+
it("returns staging templates", () => {
|
|
9
|
+
const templates = listTemplates("staging");
|
|
10
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
11
|
+
});
|
|
12
|
+
it("returns mart templates", () => {
|
|
13
|
+
const templates = listTemplates("mart");
|
|
14
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
15
|
+
});
|
|
16
|
+
it("returns test templates", () => {
|
|
17
|
+
const templates = listTemplates("test");
|
|
18
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
19
|
+
});
|
|
20
|
+
it("returns empty array for unknown category", () => {
|
|
21
|
+
const templates = listTemplates("unknown-category");
|
|
22
|
+
expect(templates).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("getTemplate", () => {
|
|
26
|
+
it("returns template by name", () => {
|
|
27
|
+
const template = getTemplate("staging-model");
|
|
28
|
+
expect(template).toBeDefined();
|
|
29
|
+
expect(template?.name).toBe("staging-model");
|
|
30
|
+
});
|
|
31
|
+
it("returns null for unknown template", () => {
|
|
32
|
+
const template = getTemplate("nonexistent-template");
|
|
33
|
+
expect(template).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("renderTemplate", () => {
|
|
37
|
+
it("renders staging template with variables", () => {
|
|
38
|
+
const rendered = renderTemplate("staging-model", {
|
|
39
|
+
source_name: "raw_data",
|
|
40
|
+
source_table: "users",
|
|
41
|
+
columns: ["user_id", "email", "created_at"],
|
|
42
|
+
});
|
|
43
|
+
expect(rendered).toContain("raw_data");
|
|
44
|
+
expect(rendered).toContain("users");
|
|
45
|
+
});
|
|
46
|
+
it("throws for unknown template", () => {
|
|
47
|
+
expect(() => renderTemplate("nonexistent", {})).toThrow("Template not found");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=templates.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,UAAU,CAAC,GAAG,EAAE;QACd,aAAa,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;YACnC,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;YAChC,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;YAChC,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,SAAS,GAAG,aAAa,CAAC,kBAAkB,CAAC,CAAC;YACpD,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC;YAC9C,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,sBAAsB,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,QAAQ,GAAG,cAAc,CAAC,eAAe,EAAE;gBAC/C,WAAW,EAAE,UAAU;gBACvB,YAAY,EAAE,OAAO;gBACrB,OAAO,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC;aAC5C,CAAC,CAAC;YAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACvC,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AASA,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqK3E"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
|
+
import { join, basename } from "path";
|
|
6
|
+
import { table } from "table";
|
|
7
|
+
import { loadConfig } from "../utils/config.js";
|
|
8
|
+
import { parseModel, detectAntiPatterns } from "../utils/analyzer.js";
|
|
9
|
+
export async function analyzeCommand(options) {
|
|
10
|
+
const spinner = ora("Analyzing dbt project...").start();
|
|
11
|
+
try {
|
|
12
|
+
const config = loadConfig(options.path);
|
|
13
|
+
const projectPath = options.path;
|
|
14
|
+
// Find all SQL files
|
|
15
|
+
const pattern = options.model
|
|
16
|
+
? `**/${options.model}.sql`
|
|
17
|
+
: "**/*.sql";
|
|
18
|
+
const files = await glob(pattern, {
|
|
19
|
+
cwd: projectPath,
|
|
20
|
+
ignore: ["**/target/**", "**/dbt_packages/**", "**/node_modules/**"],
|
|
21
|
+
});
|
|
22
|
+
if (files.length === 0) {
|
|
23
|
+
spinner.fail("No SQL files found");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
spinner.text = `Found ${files.length} SQL files. Analyzing...`;
|
|
27
|
+
const allIssues = [];
|
|
28
|
+
const modelStats = {
|
|
29
|
+
total: files.length,
|
|
30
|
+
staging: 0,
|
|
31
|
+
intermediate: 0,
|
|
32
|
+
mart: 0,
|
|
33
|
+
other: 0,
|
|
34
|
+
withTests: 0,
|
|
35
|
+
withDocs: 0,
|
|
36
|
+
};
|
|
37
|
+
for (const file of files) {
|
|
38
|
+
const filePath = join(projectPath, file);
|
|
39
|
+
const content = readFileSync(filePath, "utf-8");
|
|
40
|
+
const fileName = basename(file, ".sql");
|
|
41
|
+
// Categorize model
|
|
42
|
+
if (fileName.startsWith("stg_"))
|
|
43
|
+
modelStats.staging++;
|
|
44
|
+
else if (fileName.startsWith("int_"))
|
|
45
|
+
modelStats.intermediate++;
|
|
46
|
+
else if (fileName.startsWith("fct_") || fileName.startsWith("dim_"))
|
|
47
|
+
modelStats.mart++;
|
|
48
|
+
else
|
|
49
|
+
modelStats.other++;
|
|
50
|
+
// Parse and analyze
|
|
51
|
+
const model = parseModel(content, fileName);
|
|
52
|
+
const issues = detectAntiPatterns(model, config.warehouse);
|
|
53
|
+
for (const issue of issues) {
|
|
54
|
+
allIssues.push({ ...issue, file });
|
|
55
|
+
}
|
|
56
|
+
// Check for tests
|
|
57
|
+
const schemaPath = join(projectPath, file.replace(".sql", ".yml"));
|
|
58
|
+
const schemaPath2 = join(projectPath, "models", "schema.yml");
|
|
59
|
+
if (existsSync(schemaPath) || existsSync(schemaPath2)) {
|
|
60
|
+
modelStats.withTests++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
spinner.succeed(`Analyzed ${files.length} models`);
|
|
64
|
+
// Group issues by severity
|
|
65
|
+
const critical = allIssues.filter(i => i.severity === "critical");
|
|
66
|
+
const high = allIssues.filter(i => i.severity === "high");
|
|
67
|
+
const medium = allIssues.filter(i => i.severity === "medium");
|
|
68
|
+
const low = allIssues.filter(i => i.severity === "low");
|
|
69
|
+
if (options.json) {
|
|
70
|
+
console.log(JSON.stringify({
|
|
71
|
+
summary: {
|
|
72
|
+
totalModels: modelStats.total,
|
|
73
|
+
issues: {
|
|
74
|
+
critical: critical.length,
|
|
75
|
+
high: high.length,
|
|
76
|
+
medium: medium.length,
|
|
77
|
+
low: low.length,
|
|
78
|
+
},
|
|
79
|
+
modelTypes: modelStats,
|
|
80
|
+
},
|
|
81
|
+
issues: allIssues,
|
|
82
|
+
}, null, 2));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Print summary
|
|
86
|
+
console.log("\n" + chalk.bold("Project Summary"));
|
|
87
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
88
|
+
const summaryData = [
|
|
89
|
+
["Total Models", modelStats.total.toString()],
|
|
90
|
+
["Staging (stg_)", chalk.cyan(modelStats.staging.toString())],
|
|
91
|
+
["Intermediate (int_)", chalk.blue(modelStats.intermediate.toString())],
|
|
92
|
+
["Marts (fct_/dim_)", chalk.green(modelStats.mart.toString())],
|
|
93
|
+
["Other", chalk.gray(modelStats.other.toString())],
|
|
94
|
+
];
|
|
95
|
+
console.log(table(summaryData, {
|
|
96
|
+
border: {
|
|
97
|
+
topBody: "", topJoin: "", topLeft: "", topRight: "",
|
|
98
|
+
bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "",
|
|
99
|
+
bodyLeft: "", bodyRight: "", bodyJoin: chalk.gray("│"),
|
|
100
|
+
joinBody: "", joinLeft: "", joinRight: "", joinJoin: "",
|
|
101
|
+
},
|
|
102
|
+
}));
|
|
103
|
+
// Print issues
|
|
104
|
+
console.log(chalk.bold("\nIssues Found"));
|
|
105
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
106
|
+
const issuesSummary = [
|
|
107
|
+
[chalk.red("Critical"), critical.length.toString()],
|
|
108
|
+
[chalk.hex("#FFA500")("High"), high.length.toString()],
|
|
109
|
+
[chalk.yellow("Medium"), medium.length.toString()],
|
|
110
|
+
[chalk.gray("Low"), low.length.toString()],
|
|
111
|
+
];
|
|
112
|
+
console.log(table(issuesSummary, {
|
|
113
|
+
border: {
|
|
114
|
+
topBody: "", topJoin: "", topLeft: "", topRight: "",
|
|
115
|
+
bottomBody: "", bottomJoin: "", bottomLeft: "", bottomRight: "",
|
|
116
|
+
bodyLeft: "", bodyRight: "", bodyJoin: chalk.gray("│"),
|
|
117
|
+
joinBody: "", joinLeft: "", joinRight: "", joinJoin: "",
|
|
118
|
+
},
|
|
119
|
+
}));
|
|
120
|
+
if (allIssues.length > 0) {
|
|
121
|
+
console.log(chalk.bold("\nDetailed Issues"));
|
|
122
|
+
console.log(chalk.gray("─".repeat(50)));
|
|
123
|
+
// Show critical and high issues
|
|
124
|
+
const importantIssues = [...critical, ...high];
|
|
125
|
+
for (const issue of importantIssues.slice(0, options.verbose ? undefined : 10)) {
|
|
126
|
+
const severityColor = issue.severity === "critical" ? chalk.red : chalk.hex("#FFA500");
|
|
127
|
+
console.log(`\n${severityColor(`[${issue.severity.toUpperCase()}]`)} ${chalk.bold(issue.pattern)}`);
|
|
128
|
+
console.log(chalk.gray(` File: ${issue.file}`));
|
|
129
|
+
console.log(chalk.gray(` ${issue.explanation}`));
|
|
130
|
+
console.log(chalk.cyan(` Fix: ${issue.fix}`));
|
|
131
|
+
}
|
|
132
|
+
if (!options.verbose && importantIssues.length > 10) {
|
|
133
|
+
console.log(chalk.gray(`\n ... and ${importantIssues.length - 10} more issues. Use --verbose to see all.`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Exit code based on issues
|
|
137
|
+
if (critical.length > 0) {
|
|
138
|
+
console.log(chalk.red("\n✖ Analysis failed with critical issues"));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
else if (high.length > 0) {
|
|
142
|
+
console.log(chalk.yellow("\n⚠ Analysis completed with warnings"));
|
|
143
|
+
process.exit(0);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log(chalk.green("\n✓ Analysis passed"));
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
spinner.fail("Analysis failed");
|
|
152
|
+
console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,eAAO,MAAM,WAAW,SAwHpB,CAAC"}
|