metabase-iac 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 +206 -0
- package/bin/metabase-iac.mjs +18 -0
- package/dist/auth.d.ts +3 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +13 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +97 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +39 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +118 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/apply.d.ts +3 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +32 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +20 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/pull.d.ts +4 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +240 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -0
- package/dist/loader.d.ts +7 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +46 -0
- package/dist/loader.js.map +1 -0
- package/dist/reconciler.d.ts +11 -0
- package/dist/reconciler.d.ts.map +1 -0
- package/dist/reconciler.js +184 -0
- package/dist/reconciler.js.map +1 -0
- package/dist/resolver.d.ts +28 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +139 -0
- package/dist/resolver.js.map +1 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +26 -0
- package/dist/state.js.map +1 -0
- package/dist/types.d.ts +159 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
- package/src/auth.ts +16 -0
- package/src/cli.ts +98 -0
- package/src/client.ts +166 -0
- package/src/commands/apply.ts +42 -0
- package/src/commands/plan.ts +26 -0
- package/src/commands/pull.ts +286 -0
- package/src/config.ts +27 -0
- package/src/loader.ts +49 -0
- package/src/reconciler.ts +241 -0
- package/src/resolver.ts +150 -0
- package/src/state.ts +38 -0
- package/src/types.ts +204 -0
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# metabase-iac
|
|
2
|
+
|
|
3
|
+
Infrastructure as Code for [Metabase](https://www.metabase.com). Manage collections, questions (SQL, MongoDB, structured), dashboards, and text cards as code.
|
|
4
|
+
|
|
5
|
+
Similar to how Grafana dashboards can be managed as JSON, `metabase-iac` lets you version-control your Metabase resources and apply changes through a CLI.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add metabase-iac
|
|
11
|
+
# or
|
|
12
|
+
npm install metabase-iac
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 1. Create a new directory for your Metabase instance
|
|
19
|
+
mkdir metabase-prod && cd metabase-prod
|
|
20
|
+
pnpm init && pnpm add metabase-iac
|
|
21
|
+
|
|
22
|
+
# 2. Configure credentials
|
|
23
|
+
cat > .env <<EOF
|
|
24
|
+
METABASE_URL=https://your-metabase.example.com
|
|
25
|
+
METABASE_USERNAME=admin@example.com
|
|
26
|
+
METABASE_PASSWORD=your-password
|
|
27
|
+
EOF
|
|
28
|
+
|
|
29
|
+
# 3. Pull all existing resources from Metabase
|
|
30
|
+
npx metabase-iac pull
|
|
31
|
+
|
|
32
|
+
# 4. You now have collections/, questions/, dashboards/ — commit them to git
|
|
33
|
+
git init && git add -A && git commit -m "Initial pull from Metabase"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
| Command | Description |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `metabase-iac pull` | Import collections, questions, and dashboards from Metabase |
|
|
41
|
+
| `metabase-iac plan` | Preview what changes would be applied (dry run) |
|
|
42
|
+
| `metabase-iac apply` | Push local resource definitions to Metabase |
|
|
43
|
+
| `metabase-iac databases` | List connected databases |
|
|
44
|
+
| `metabase-iac collections` | List collections |
|
|
45
|
+
|
|
46
|
+
### Options
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
pull
|
|
50
|
+
-o, --output <dir> Output directory (default: ".")
|
|
51
|
+
--import-base <path> Import path for types in generated files (default: "metabase-iac/types")
|
|
52
|
+
|
|
53
|
+
plan / apply
|
|
54
|
+
-d, --dir <dir> Resources directory (default: ".")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Authentication
|
|
58
|
+
|
|
59
|
+
Configure via environment variables or a `.env` file:
|
|
60
|
+
|
|
61
|
+
```env
|
|
62
|
+
METABASE_URL=https://your-metabase.example.com
|
|
63
|
+
|
|
64
|
+
# Option 1: Username + password
|
|
65
|
+
METABASE_USERNAME=admin@example.com
|
|
66
|
+
METABASE_PASSWORD=your-password
|
|
67
|
+
|
|
68
|
+
# Option 2: Session token
|
|
69
|
+
METABASE_SESSION_TOKEN=your-session-token
|
|
70
|
+
|
|
71
|
+
# Option 3: API key
|
|
72
|
+
METABASE_API_KEY=mb_xxxx
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Resource types
|
|
76
|
+
|
|
77
|
+
### Collections
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { collection } from 'metabase-iac/types'
|
|
81
|
+
|
|
82
|
+
export default collection({
|
|
83
|
+
id: 23,
|
|
84
|
+
slug: 'engineering',
|
|
85
|
+
name: 'Engineering',
|
|
86
|
+
description: 'Engineering team dashboards',
|
|
87
|
+
parent: 'Teams', // optional — omit for root
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Native queries (SQL)
|
|
92
|
+
|
|
93
|
+
Two files: a `.sql` with the query and a `.meta.json` with metadata.
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
-- questions/daily-signups.sql
|
|
97
|
+
SELECT date_trunc('day', created_at) AS day, count(*)
|
|
98
|
+
FROM users
|
|
99
|
+
GROUP BY 1
|
|
100
|
+
ORDER BY 1 DESC
|
|
101
|
+
LIMIT 30
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"id": 42,
|
|
107
|
+
"name": "Daily Signups",
|
|
108
|
+
"collection": "Engineering",
|
|
109
|
+
"database": "production",
|
|
110
|
+
"display": "line",
|
|
111
|
+
"description": "Daily user signups over the last 30 days"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Native queries (MongoDB)
|
|
116
|
+
|
|
117
|
+
Same pattern, with `.mongodb` extension:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
[
|
|
121
|
+
{ "$match": { "status": "active" } },
|
|
122
|
+
{ "$group": { "_id": "$region", "count": { "$sum": 1 } } },
|
|
123
|
+
{ "$sort": { "count": -1 } }
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Structured queries (MBQL)
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { structuredQuestion } from 'metabase-iac/types'
|
|
131
|
+
|
|
132
|
+
export default structuredQuestion({
|
|
133
|
+
id: 37,
|
|
134
|
+
name: 'accounts-count',
|
|
135
|
+
collection: 'Engineering',
|
|
136
|
+
database: 'production',
|
|
137
|
+
display: 'scalar',
|
|
138
|
+
datasetQuery: {
|
|
139
|
+
stages: [{ aggregation: [["count", {}]], "source-table": 77 }],
|
|
140
|
+
database: 2,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Dashboards
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { dashboard } from 'metabase-iac/types'
|
|
149
|
+
|
|
150
|
+
export default dashboard({
|
|
151
|
+
id: 3,
|
|
152
|
+
name: 'Engineering Overview',
|
|
153
|
+
collection: 'Engineering',
|
|
154
|
+
cards: [
|
|
155
|
+
{ question: 'Daily Signups', questionId: 42, width: 12, height: 6, row: 0, col: 0 },
|
|
156
|
+
{ question: 'accounts-count', questionId: 37, width: 6, height: 3, row: 0, col: 12 },
|
|
157
|
+
{ text: '## Notes\nUpdated daily.', width: 24, height: 2, row: 6, col: 0 },
|
|
158
|
+
],
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Workflow
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
pull → review diff → edit → plan → apply → commit
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
1. `metabase-iac pull` — import current state from Metabase
|
|
169
|
+
2. Review changes with `git diff`
|
|
170
|
+
3. Edit `.ts`/`.sql`/`.mongodb` files as needed
|
|
171
|
+
4. `metabase-iac plan` — preview what will change
|
|
172
|
+
5. `metabase-iac apply` — push to Metabase
|
|
173
|
+
6. `git commit` — version control your changes
|
|
174
|
+
|
|
175
|
+
## Project structure
|
|
176
|
+
|
|
177
|
+
A consumer repo looks like this:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
my-metabase/
|
|
181
|
+
├── collections/
|
|
182
|
+
│ └── *.ts
|
|
183
|
+
├── questions/
|
|
184
|
+
│ ├── *.ts # Structured (MBQL) queries
|
|
185
|
+
│ ├── *.sql # Native SQL queries
|
|
186
|
+
│ ├── *.mongodb # Native MongoDB queries
|
|
187
|
+
│ └── *.meta.json # Metadata for native queries
|
|
188
|
+
├── dashboards/
|
|
189
|
+
│ └── *.ts
|
|
190
|
+
├── .env # Credentials (gitignored)
|
|
191
|
+
├── .gitignore
|
|
192
|
+
├── package.json
|
|
193
|
+
└── tsconfig.json # Optional — for IDE type checking
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## How it works
|
|
197
|
+
|
|
198
|
+
- **Pull** reads from the Metabase REST API and generates typed resource files
|
|
199
|
+
- **Plan/Apply** loads local resource files, diffs against a state file (`.metabase-state.json`), and creates/updates resources via the API
|
|
200
|
+
- Resources are matched by name within their collection
|
|
201
|
+
- Ordering is automatic: collections are created before questions, questions before dashboards
|
|
202
|
+
- Both SQL and MongoDB databases are fully supported — native queries are stored as plain text files
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { createRequire } from 'node:module'
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const require = createRequire(join(__dirname, '..', 'package.json'))
|
|
10
|
+
const tsxPath = require.resolve('tsx')
|
|
11
|
+
const cli = join(__dirname, '..', 'dist', 'cli.js')
|
|
12
|
+
|
|
13
|
+
const child = spawn(process.execPath, ['--import', tsxPath, cli, ...process.argv.slice(2)], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
cwd: process.cwd(),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
child.on('exit', (code) => process.exit(code ?? 0))
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAG5C,wBAAsB,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC,CAY5D"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MetabaseClient } from './client.js';
|
|
2
|
+
import { loadConfig } from './config.js';
|
|
3
|
+
export async function createClient() {
|
|
4
|
+
const config = loadConfig();
|
|
5
|
+
if (config.apiKey) {
|
|
6
|
+
return MetabaseClient.withApiKey(config.url, config.apiKey);
|
|
7
|
+
}
|
|
8
|
+
if (config.sessionToken) {
|
|
9
|
+
return MetabaseClient.withSession(config.url, config.sessionToken);
|
|
10
|
+
}
|
|
11
|
+
return MetabaseClient.login(config.url, config.username, config.password);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAE3B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IAC7D,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;IACpE,CAAC;IAED,OAAO,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,QAAS,EAAE,MAAM,CAAC,QAAS,CAAC,CAAA;AAC7E,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { createClient } from './auth.js';
|
|
5
|
+
import { apply } from './commands/apply.js';
|
|
6
|
+
import { pull } from './commands/pull.js';
|
|
7
|
+
import { saveState } from './state.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('metabase-iac')
|
|
11
|
+
.description('Infrastructure as Code for Metabase')
|
|
12
|
+
.version('0.1.0');
|
|
13
|
+
program
|
|
14
|
+
.command('pull')
|
|
15
|
+
.description('Import all collections, questions, dashboards from Metabase into local files')
|
|
16
|
+
.option('-o, --output <dir>', 'Output directory', '.')
|
|
17
|
+
.option('--import-base <path>', 'Import path for types in generated files', 'metabase-iac/types')
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
try {
|
|
20
|
+
const client = await createClient();
|
|
21
|
+
console.log(chalk.bold('Pulling from Metabase...\n'));
|
|
22
|
+
const state = await pull(client, opts.output, opts.importBase);
|
|
23
|
+
await saveState(state);
|
|
24
|
+
console.log(chalk.green(`\nPull complete! ${state.length} resource(s) saved to ${opts.output}/`));
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
program
|
|
32
|
+
.command('plan')
|
|
33
|
+
.description('Show what changes would be applied (dry run)')
|
|
34
|
+
.option('-d, --dir <dir>', 'Resources directory', '.')
|
|
35
|
+
.action(async (opts) => {
|
|
36
|
+
try {
|
|
37
|
+
const client = await createClient();
|
|
38
|
+
await apply(client, true, opts.dir);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
program
|
|
46
|
+
.command('apply')
|
|
47
|
+
.description('Apply resource definitions to Metabase')
|
|
48
|
+
.option('-d, --dir <dir>', 'Resources directory', '.')
|
|
49
|
+
.action(async (opts) => {
|
|
50
|
+
try {
|
|
51
|
+
const client = await createClient();
|
|
52
|
+
await apply(client, false, opts.dir);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command('databases')
|
|
61
|
+
.description('List available databases in Metabase')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
try {
|
|
64
|
+
const client = await createClient();
|
|
65
|
+
const dbs = await client.getDatabases();
|
|
66
|
+
console.log(chalk.bold('Databases:\n'));
|
|
67
|
+
for (const db of dbs) {
|
|
68
|
+
console.log(` ${chalk.cyan(db.id.toString().padStart(3))} ${db.name} (${db.engine})`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
program
|
|
77
|
+
.command('collections')
|
|
78
|
+
.description('List available collections in Metabase')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
try {
|
|
81
|
+
const client = await createClient();
|
|
82
|
+
const collections = await client.getCollections();
|
|
83
|
+
console.log(chalk.bold('Collections:\n'));
|
|
84
|
+
for (const c of collections) {
|
|
85
|
+
if (c.archived)
|
|
86
|
+
continue;
|
|
87
|
+
const parent = c.parent_id ? ` (parent: ${c.parent_id})` : '';
|
|
88
|
+
console.log(` ${chalk.cyan(c.id.toString().padStart(3))} ${c.name}${chalk.gray(parent)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
program.parse();
|
|
97
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { MetabaseCard, MetabaseCollection, MetabaseDashboard, MetabaseDatabase } from './types.js';
|
|
2
|
+
export declare class MetabaseClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private headers;
|
|
5
|
+
constructor(baseUrl: string, auth: {
|
|
6
|
+
sessionToken?: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
});
|
|
9
|
+
static login(baseUrl: string, username: string, password: string): Promise<MetabaseClient>;
|
|
10
|
+
static withApiKey(baseUrl: string, apiKey: string): MetabaseClient;
|
|
11
|
+
static withSession(baseUrl: string, sessionToken: string): MetabaseClient;
|
|
12
|
+
getCollections(): Promise<MetabaseCollection[]>;
|
|
13
|
+
createCollection(data: {
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
parent_id?: number | null;
|
|
17
|
+
}): Promise<MetabaseCollection>;
|
|
18
|
+
updateCollection(id: number, data: {
|
|
19
|
+
name?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
parent_id?: number | null;
|
|
22
|
+
}): Promise<MetabaseCollection>;
|
|
23
|
+
getCards(): Promise<MetabaseCard[]>;
|
|
24
|
+
getCard(id: number): Promise<MetabaseCard>;
|
|
25
|
+
createCard(data: Record<string, unknown>): Promise<MetabaseCard>;
|
|
26
|
+
updateCard(id: number, data: Record<string, unknown>): Promise<MetabaseCard>;
|
|
27
|
+
getDashboards(): Promise<MetabaseDashboard[]>;
|
|
28
|
+
getDashboard(id: number): Promise<MetabaseDashboard>;
|
|
29
|
+
createDashboard(data: Record<string, unknown>): Promise<MetabaseDashboard>;
|
|
30
|
+
updateDashboard(id: number, data: Record<string, unknown>): Promise<MetabaseDashboard>;
|
|
31
|
+
addCardToDashboard(dashboardId: number, data: Record<string, unknown>): Promise<unknown>;
|
|
32
|
+
updateDashboardCards(dashboardId: number, cards: Record<string, unknown>[]): Promise<unknown>;
|
|
33
|
+
getDatabases(): Promise<MetabaseDatabase[]>;
|
|
34
|
+
getDatabaseMetadata(id: number): Promise<MetabaseDatabase>;
|
|
35
|
+
get<T = any>(path: string): Promise<T>;
|
|
36
|
+
private post;
|
|
37
|
+
private put;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,YAAY,CAAA;AAEnB,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;WAWhE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAiBhG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc;IAIlE,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc;IAMnE,cAAc,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAI/C,gBAAgB,CAAC,IAAI,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAC1B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIzB,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACvE,OAAO,CAAC,kBAAkB,CAAC;IAMxB,QAAQ,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAInC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI1C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAIhE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAM5E,aAAa,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAI7C,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIpD,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI1E,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAItF,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC;IAIb,oBAAoB,CACxB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAC/B,OAAO,CAAC,OAAO,CAAC;IAMb,YAAY,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAK3C,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAM1D,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;YAS9B,IAAI;YAaJ,GAAG;CAYlB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export class MetabaseClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
headers;
|
|
4
|
+
constructor(baseUrl, auth) {
|
|
5
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
6
|
+
this.headers = { 'Content-Type': 'application/json' };
|
|
7
|
+
if (auth.apiKey) {
|
|
8
|
+
this.headers['x-api-key'] = auth.apiKey;
|
|
9
|
+
}
|
|
10
|
+
else if (auth.sessionToken) {
|
|
11
|
+
this.headers['X-Metabase-Session'] = auth.sessionToken;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
static async login(baseUrl, username, password) {
|
|
15
|
+
const url = `${baseUrl.replace(/\/$/, '')}/api/session`;
|
|
16
|
+
const res = await fetch(url, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
body: JSON.stringify({ username, password }),
|
|
20
|
+
});
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const text = await res.text();
|
|
23
|
+
throw new Error(`Login failed (${res.status}): ${text}`);
|
|
24
|
+
}
|
|
25
|
+
const { id } = (await res.json());
|
|
26
|
+
return new MetabaseClient(baseUrl, { sessionToken: id });
|
|
27
|
+
}
|
|
28
|
+
static withApiKey(baseUrl, apiKey) {
|
|
29
|
+
return new MetabaseClient(baseUrl, { apiKey });
|
|
30
|
+
}
|
|
31
|
+
static withSession(baseUrl, sessionToken) {
|
|
32
|
+
return new MetabaseClient(baseUrl, { sessionToken });
|
|
33
|
+
}
|
|
34
|
+
// ─── Collections ───
|
|
35
|
+
async getCollections() {
|
|
36
|
+
return this.get('/api/collection');
|
|
37
|
+
}
|
|
38
|
+
async createCollection(data) {
|
|
39
|
+
return this.post('/api/collection', data);
|
|
40
|
+
}
|
|
41
|
+
async updateCollection(id, data) {
|
|
42
|
+
return this.put(`/api/collection/${id}`, data);
|
|
43
|
+
}
|
|
44
|
+
// ─── Cards (Questions) ───
|
|
45
|
+
async getCards() {
|
|
46
|
+
return this.get('/api/card');
|
|
47
|
+
}
|
|
48
|
+
async getCard(id) {
|
|
49
|
+
return this.get(`/api/card/${id}`);
|
|
50
|
+
}
|
|
51
|
+
async createCard(data) {
|
|
52
|
+
return this.post('/api/card', data);
|
|
53
|
+
}
|
|
54
|
+
async updateCard(id, data) {
|
|
55
|
+
return this.put(`/api/card/${id}`, data);
|
|
56
|
+
}
|
|
57
|
+
// ─── Dashboards ───
|
|
58
|
+
async getDashboards() {
|
|
59
|
+
return this.get('/api/dashboard');
|
|
60
|
+
}
|
|
61
|
+
async getDashboard(id) {
|
|
62
|
+
return this.get(`/api/dashboard/${id}`);
|
|
63
|
+
}
|
|
64
|
+
async createDashboard(data) {
|
|
65
|
+
return this.post('/api/dashboard', data);
|
|
66
|
+
}
|
|
67
|
+
async updateDashboard(id, data) {
|
|
68
|
+
return this.put(`/api/dashboard/${id}`, data);
|
|
69
|
+
}
|
|
70
|
+
async addCardToDashboard(dashboardId, data) {
|
|
71
|
+
return this.post(`/api/dashboard/${dashboardId}/cards`, data);
|
|
72
|
+
}
|
|
73
|
+
async updateDashboardCards(dashboardId, cards) {
|
|
74
|
+
return this.put(`/api/dashboard/${dashboardId}/cards`, { cards });
|
|
75
|
+
}
|
|
76
|
+
// ─── Databases ───
|
|
77
|
+
async getDatabases() {
|
|
78
|
+
const result = await this.get('/api/database');
|
|
79
|
+
return result.data;
|
|
80
|
+
}
|
|
81
|
+
async getDatabaseMetadata(id) {
|
|
82
|
+
return this.get(`/api/database/${id}/metadata`);
|
|
83
|
+
}
|
|
84
|
+
// ─── HTTP helpers ───
|
|
85
|
+
async get(path) {
|
|
86
|
+
const res = await fetch(`${this.baseUrl}${path}`, { headers: this.headers });
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
throw new Error(`GET ${path} failed (${res.status}): ${text}`);
|
|
90
|
+
}
|
|
91
|
+
return res.json();
|
|
92
|
+
}
|
|
93
|
+
async post(path, body) {
|
|
94
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: this.headers,
|
|
97
|
+
body: JSON.stringify(body),
|
|
98
|
+
});
|
|
99
|
+
if (!res.ok) {
|
|
100
|
+
const text = await res.text();
|
|
101
|
+
throw new Error(`POST ${path} failed (${res.status}): ${text}`);
|
|
102
|
+
}
|
|
103
|
+
return res.json();
|
|
104
|
+
}
|
|
105
|
+
async put(path, body) {
|
|
106
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
107
|
+
method: 'PUT',
|
|
108
|
+
headers: this.headers,
|
|
109
|
+
body: JSON.stringify(body),
|
|
110
|
+
});
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
const text = await res.text();
|
|
113
|
+
throw new Error(`PUT ${path} failed (${res.status}): ${text}`);
|
|
114
|
+
}
|
|
115
|
+
return res.json();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAOlD,wBAAsB,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAE,MAAY,iBAiC7F"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { Resolver } from '../resolver.js';
|
|
3
|
+
import { loadResources } from '../loader.js';
|
|
4
|
+
import { loadState, saveState } from '../state.js';
|
|
5
|
+
import { buildPlan, applyActions } from '../reconciler.js';
|
|
6
|
+
import { printPlan } from './plan.js';
|
|
7
|
+
export async function apply(client, dryRun, resourceDir = '.') {
|
|
8
|
+
console.log('Loading resources...');
|
|
9
|
+
const resources = await loadResources(resourceDir);
|
|
10
|
+
if (resources.length === 0) {
|
|
11
|
+
console.log(chalk.yellow('No resources found. Expected collections/, questions/, dashboards/ subdirectories.'));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
console.log(`Found ${resources.length} resource(s)`);
|
|
15
|
+
console.log('Initializing resolver...');
|
|
16
|
+
const resolver = new Resolver();
|
|
17
|
+
await resolver.init(client);
|
|
18
|
+
const state = await loadState();
|
|
19
|
+
const actions = buildPlan(resources, state);
|
|
20
|
+
printPlan(actions);
|
|
21
|
+
if (dryRun) {
|
|
22
|
+
console.log(chalk.cyan('Dry run — no changes applied.'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (actions.length === 0)
|
|
26
|
+
return;
|
|
27
|
+
console.log(chalk.bold('Applying changes...'));
|
|
28
|
+
const newState = await applyActions(client, resolver, actions, state);
|
|
29
|
+
await saveState(newState);
|
|
30
|
+
console.log(chalk.green(`\nDone! Applied ${actions.length} change(s).`));
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,MAAsB,EAAE,MAAe,EAAE,cAAsB,GAAG;IAC5F,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;IACnC,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAA;IAElD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,oFAAoF,CAAC,CAAC,CAAA;QAC/G,OAAM;IACR,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,SAAS,SAAS,CAAC,MAAM,cAAc,CAAC,CAAA;IAEpD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC/B,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAE3B,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAA;IAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAE3C,SAAS,CAAC,OAAO,CAAC,CAAA;IAElB,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC,CAAA;QACxD,OAAM;IACR,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAEhC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAA;IAE9C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;IACrE,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAA;IAEzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,aAAa,CAAC,CAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAE9C,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAsB1C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export function printPlan(actions) {
|
|
3
|
+
if (actions.length === 0) {
|
|
4
|
+
console.log(chalk.green('No changes needed. Everything is up to date.'));
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
console.log(chalk.bold(`\nPlan: ${actions.length} action(s)\n`));
|
|
8
|
+
for (const action of actions) {
|
|
9
|
+
const icon = action.type === 'create' ? chalk.green('+') : chalk.yellow('~');
|
|
10
|
+
const verb = action.type === 'create' ? chalk.green('create') : chalk.yellow('update');
|
|
11
|
+
const name = action.resource.name;
|
|
12
|
+
const kind = action.resource.kind;
|
|
13
|
+
console.log(` ${icon} ${verb} ${kind}: ${chalk.bold(name)}`);
|
|
14
|
+
if (action.existingId) {
|
|
15
|
+
console.log(chalk.gray(` (id: ${action.existingId})`));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
console.log();
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,UAAU,SAAS,CAAC,OAAiB;IACzC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC,CAAA;QACxE,OAAM;IACR,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,MAAM,cAAc,CAAC,CAAC,CAAA;IAEhE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtF,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QACjC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;QAEjC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAE7D,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,EAAE,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,KAAK,EAIV,SAAS,EACV,MAAM,aAAa,CAAA;AAoBpB,wBAAsB,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAqH9G"}
|