dataiku-sdk 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/bin/dss.js +2 -0
- package/dist/packages/types/src/index.d.ts +458 -0
- package/dist/packages/types/src/index.js +384 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +689 -0
- package/dist/src/client.d.ts +89 -0
- package/dist/src/client.js +301 -0
- package/dist/src/errors.d.ts +29 -0
- package/dist/src/errors.js +141 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +24 -0
- package/dist/src/resources/base.d.ts +7 -0
- package/dist/src/resources/base.js +12 -0
- package/dist/src/resources/code-envs.d.ts +8 -0
- package/dist/src/resources/code-envs.js +36 -0
- package/dist/src/resources/connections.d.ts +20 -0
- package/dist/src/resources/connections.js +77 -0
- package/dist/src/resources/datasets.d.ts +57 -0
- package/dist/src/resources/datasets.js +423 -0
- package/dist/src/resources/folders.d.ts +15 -0
- package/dist/src/resources/folders.js +58 -0
- package/dist/src/resources/jobs.d.ts +72 -0
- package/dist/src/resources/jobs.js +184 -0
- package/dist/src/resources/notebooks.d.ts +34 -0
- package/dist/src/resources/notebooks.js +75 -0
- package/dist/src/resources/projects.d.ts +38 -0
- package/dist/src/resources/projects.js +185 -0
- package/dist/src/resources/recipes.d.ts +35 -0
- package/dist/src/resources/recipes.js +281 -0
- package/dist/src/resources/scenarios.d.ts +26 -0
- package/dist/src/resources/scenarios.js +57 -0
- package/dist/src/resources/sql.d.ts +40 -0
- package/dist/src/resources/sql.js +40 -0
- package/dist/src/resources/variables.d.ts +10 -0
- package/dist/src/resources/variables.js +22 -0
- package/dist/src/schemas.d.ts +7 -0
- package/dist/src/schemas.js +6 -0
- package/dist/src/utils/deep-merge.d.ts +1 -0
- package/dist/src/utils/deep-merge.js +15 -0
- package/dist/src/utils/flow-map.d.ts +37 -0
- package/dist/src/utils/flow-map.js +296 -0
- package/dist/src/utils/pagination.d.ts +8 -0
- package/dist/src/utils/pagination.js +23 -0
- package/dist/src/utils/sanitize.d.ts +2 -0
- package/dist/src/utils/sanitize.js +16 -0
- package/package.json +47 -0
- package/packages/types/dist/index.d.ts +458 -0
- package/packages/types/dist/index.js +384 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { writeFile, } from "node:fs/promises";
|
|
2
|
+
import { resolve, } from "node:path";
|
|
3
|
+
import { DataikuError, } from "../errors.js";
|
|
4
|
+
import { RecipeSummaryArraySchema, } from "../schemas.js";
|
|
5
|
+
import { sanitizeFileName, } from "../utils/sanitize.js";
|
|
6
|
+
import { BaseResource, } from "./base.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Helpers: type narrowing
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
function asString(value) {
|
|
11
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
function asStringArray(value) {
|
|
14
|
+
if (!Array.isArray(value))
|
|
15
|
+
return undefined;
|
|
16
|
+
const out = value.filter((v) => typeof v === "string" && v.length > 0);
|
|
17
|
+
return out.length > 0 ? out : undefined;
|
|
18
|
+
}
|
|
19
|
+
function asRecord(value) {
|
|
20
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
21
|
+
return undefined;
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Helpers: retry predicate
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
function shouldRetryRecipeCreateWithOutputProvisioning(error) {
|
|
28
|
+
if (!(error instanceof DataikuError))
|
|
29
|
+
return false;
|
|
30
|
+
if (error.category !== "validation"
|
|
31
|
+
&& error.category !== "not_found"
|
|
32
|
+
&& error.category !== "unknown") {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const detail = `${error.statusText}\n${error.body}`.toLowerCase();
|
|
36
|
+
const mentionsMissingDataset = detail.includes("dataset")
|
|
37
|
+
&& (detail.includes("not found")
|
|
38
|
+
|| detail.includes("does not exist")
|
|
39
|
+
|| detail.includes("unknown"));
|
|
40
|
+
return mentionsMissingDataset;
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Resource
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
export class RecipesResource extends BaseResource {
|
|
46
|
+
/** List all recipes in a project. */
|
|
47
|
+
async list(projectKey) {
|
|
48
|
+
const enc = this.enc(projectKey);
|
|
49
|
+
const raw = await this.client.get(`/public/api/projects/${enc}/recipes/`);
|
|
50
|
+
return this.client.safeParse(RecipeSummaryArraySchema, raw, "recipes.list");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a recipe definition (and optionally its payload).
|
|
54
|
+
* Returns the raw API response shape: `{ recipe, payload }`.
|
|
55
|
+
*/
|
|
56
|
+
get(recipeName, opts) {
|
|
57
|
+
const enc = this.enc(opts?.projectKey);
|
|
58
|
+
const rnEnc = encodeURIComponent(recipeName);
|
|
59
|
+
const params = new URLSearchParams();
|
|
60
|
+
if (opts?.includePayload)
|
|
61
|
+
params.set("includePayload", "true");
|
|
62
|
+
// oxlint-disable-next-line eqeqeq -- intentional null check
|
|
63
|
+
if (opts?.payloadMaxLines != null)
|
|
64
|
+
params.set("payloadMaxLines", String(opts.payloadMaxLines));
|
|
65
|
+
const qs = params.toString();
|
|
66
|
+
const url = `/public/api/projects/${enc}/recipes/${rnEnc}${qs ? `?${qs}` : ""}`;
|
|
67
|
+
return this.client.get(url);
|
|
68
|
+
}
|
|
69
|
+
/** Create a recipe, with optional output dataset provisioning and join configuration. */
|
|
70
|
+
async create(opts) {
|
|
71
|
+
const pk = this.resolveProjectKey(opts.projectKey);
|
|
72
|
+
const enc = encodeURIComponent(pk);
|
|
73
|
+
const { type, payload, outputConnection: rawConnection, joinType: rawJoinType, } = opts;
|
|
74
|
+
// Build inputs/outputs from simple form (inputDatasets + outputDataset) or
|
|
75
|
+
// advanced form (inputs + outputs); both may coexist — simple form wins when
|
|
76
|
+
// the advanced form is absent.
|
|
77
|
+
const inputDatasets = asStringArray(opts.inputDatasets);
|
|
78
|
+
const outputDataset = asString(opts.outputDataset);
|
|
79
|
+
let inputs = asRecord(opts.inputs);
|
|
80
|
+
let outputs = asRecord(opts.outputs);
|
|
81
|
+
if (!inputs && inputDatasets) {
|
|
82
|
+
inputs = {
|
|
83
|
+
main: {
|
|
84
|
+
items: inputDatasets.map((ref) => ({ ref, deps: [], })),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!outputs && outputDataset) {
|
|
89
|
+
outputs = {
|
|
90
|
+
main: {
|
|
91
|
+
items: [{ ref: outputDataset, appendMode: false, },],
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// Auto-generate name if not provided
|
|
96
|
+
const name = opts.name ?? (type && outputDataset ? `${type}_${outputDataset}` : undefined);
|
|
97
|
+
if (!type || !name || !inputs || !outputs) {
|
|
98
|
+
throw new Error("type and (inputDatasets + outputDataset) or (name + inputs + outputs) are required for create.");
|
|
99
|
+
}
|
|
100
|
+
const recipePrototype = {
|
|
101
|
+
type,
|
|
102
|
+
name,
|
|
103
|
+
projectKey: pk,
|
|
104
|
+
inputs,
|
|
105
|
+
outputs,
|
|
106
|
+
};
|
|
107
|
+
const creationSettings = {};
|
|
108
|
+
if (payload !== undefined) {
|
|
109
|
+
creationSettings.script = payload;
|
|
110
|
+
}
|
|
111
|
+
const createRecipe = () => this.client.post(`/public/api/projects/${enc}/recipes/`, {
|
|
112
|
+
recipePrototype,
|
|
113
|
+
creationSettings,
|
|
114
|
+
});
|
|
115
|
+
const createdDatasets = [];
|
|
116
|
+
let usedOutputProvisioningFallback = false;
|
|
117
|
+
try {
|
|
118
|
+
await createRecipe();
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (!shouldRetryRecipeCreateWithOutputProvisioning(error)) {
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
usedOutputProvisioningFallback = true;
|
|
125
|
+
// Fetch existing datasets to infer output connection and type
|
|
126
|
+
const existingDs = await this.client.get(`/public/api/projects/${enc}/datasets/`);
|
|
127
|
+
let outputConnection = asString(rawConnection);
|
|
128
|
+
if (!outputConnection) {
|
|
129
|
+
const managedDs = existingDs.find((d) => d.managed && d.params?.connection);
|
|
130
|
+
if (managedDs?.params?.connection) {
|
|
131
|
+
outputConnection = managedDs.params.connection;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (outputConnection) {
|
|
135
|
+
const existingNames = new Set(existingDs.map((d) => d.name));
|
|
136
|
+
const connectionSample = existingDs.find((d) => d.params?.connection === outputConnection && d.type);
|
|
137
|
+
const inferredOutputType = connectionSample?.type ?? "Filesystem";
|
|
138
|
+
const outputRoles = outputs;
|
|
139
|
+
for (const role of Object.values(outputRoles)) {
|
|
140
|
+
for (const item of role.items ?? []) {
|
|
141
|
+
if (item.ref && !existingNames.has(item.ref)) {
|
|
142
|
+
const datasetBody = inferredOutputType === "Filesystem"
|
|
143
|
+
? {
|
|
144
|
+
projectKey: pk,
|
|
145
|
+
name: item.ref,
|
|
146
|
+
type: inferredOutputType,
|
|
147
|
+
params: {
|
|
148
|
+
connection: outputConnection,
|
|
149
|
+
path: `\${projectKey}/${item.ref}`,
|
|
150
|
+
},
|
|
151
|
+
formatType: "csv",
|
|
152
|
+
formatParams: {
|
|
153
|
+
style: "excel",
|
|
154
|
+
charset: "utf8",
|
|
155
|
+
separator: "\t",
|
|
156
|
+
quoteChar: '"',
|
|
157
|
+
escapeChar: "\\",
|
|
158
|
+
dateSerializationFormat: "ISO",
|
|
159
|
+
arrayMapFormat: "json",
|
|
160
|
+
parseHeaderRow: true,
|
|
161
|
+
compress: "gz",
|
|
162
|
+
},
|
|
163
|
+
managed: true,
|
|
164
|
+
}
|
|
165
|
+
: {
|
|
166
|
+
projectKey: pk,
|
|
167
|
+
name: item.ref,
|
|
168
|
+
type: inferredOutputType,
|
|
169
|
+
params: {
|
|
170
|
+
connection: outputConnection,
|
|
171
|
+
mode: "table",
|
|
172
|
+
table: item.ref,
|
|
173
|
+
...(connectionSample?.params?.schema
|
|
174
|
+
? { schema: connectionSample.params.schema, }
|
|
175
|
+
: {}),
|
|
176
|
+
...(connectionSample?.params?.catalog
|
|
177
|
+
? { catalog: connectionSample.params.catalog, }
|
|
178
|
+
: {}),
|
|
179
|
+
},
|
|
180
|
+
managed: connectionSample?.managed ?? false,
|
|
181
|
+
};
|
|
182
|
+
await this.client.post(`/public/api/projects/${enc}/datasets/`, datasetBody);
|
|
183
|
+
existingNames.add(item.ref);
|
|
184
|
+
createdDatasets.push(item.ref);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
await createRecipe();
|
|
190
|
+
}
|
|
191
|
+
// For join recipes: configure join conditions after creation
|
|
192
|
+
let joinConfigured = false;
|
|
193
|
+
const joinCols = typeof opts.joinOn === "string" ? [opts.joinOn,] : asStringArray(opts.joinOn);
|
|
194
|
+
const joinType = asString(rawJoinType) ?? "LEFT";
|
|
195
|
+
if (type === "join" && joinCols?.length) {
|
|
196
|
+
const rnEnc = encodeURIComponent(name);
|
|
197
|
+
const full = await this.client.get(`/public/api/projects/${enc}/recipes/${rnEnc}`);
|
|
198
|
+
// DSS returns empty payload for fresh join recipes — construct from scratch
|
|
199
|
+
const inputCount = inputDatasets?.length
|
|
200
|
+
?? inputs?.main?.items?.length
|
|
201
|
+
?? 2;
|
|
202
|
+
const virtualInputs = [{ index: 0, preFilter: {}, },];
|
|
203
|
+
for (let i = 1; i < inputCount; i++) {
|
|
204
|
+
virtualInputs.push({
|
|
205
|
+
index: i,
|
|
206
|
+
on: joinCols.map((col) => ({
|
|
207
|
+
column: col,
|
|
208
|
+
type: "string",
|
|
209
|
+
related: col,
|
|
210
|
+
relatedType: "string",
|
|
211
|
+
maxMatches: 1,
|
|
212
|
+
})),
|
|
213
|
+
joinType,
|
|
214
|
+
preFilter: {},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const joinPayload = {
|
|
218
|
+
virtualInputs,
|
|
219
|
+
computedColumns: [],
|
|
220
|
+
postFilter: {},
|
|
221
|
+
};
|
|
222
|
+
// Ensure inputs/outputs are set — DSS may not persist them from the POST for join recipes
|
|
223
|
+
const updatedFull = {
|
|
224
|
+
...full,
|
|
225
|
+
recipe: {
|
|
226
|
+
...full.recipe,
|
|
227
|
+
inputs,
|
|
228
|
+
outputs,
|
|
229
|
+
},
|
|
230
|
+
payload: JSON.stringify(joinPayload),
|
|
231
|
+
};
|
|
232
|
+
await this.client.put(`/public/api/projects/${enc}/recipes/${rnEnc}`, updatedFull);
|
|
233
|
+
joinConfigured = true;
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
recipeName: name,
|
|
237
|
+
type,
|
|
238
|
+
createdDatasets,
|
|
239
|
+
joinConfigured,
|
|
240
|
+
outputProvisioningFallbackUsed: usedOutputProvisioningFallback,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Update a recipe by merging the patch into the current definition.
|
|
245
|
+
* The `recipe` sub-object is spread-merged to preserve nested fields.
|
|
246
|
+
*/
|
|
247
|
+
async update(recipeName, data, projectKey) {
|
|
248
|
+
const enc = this.enc(projectKey);
|
|
249
|
+
const rnEnc = encodeURIComponent(recipeName);
|
|
250
|
+
const current = await this.client.get(`/public/api/projects/${enc}/recipes/${rnEnc}`);
|
|
251
|
+
const currentRecipe = asRecord(current.recipe);
|
|
252
|
+
if (!currentRecipe) {
|
|
253
|
+
throw new Error(`Recipe "${recipeName}" was not found or returned an empty definition.`);
|
|
254
|
+
}
|
|
255
|
+
const mergedRecipe = {
|
|
256
|
+
...currentRecipe,
|
|
257
|
+
...data.recipe,
|
|
258
|
+
};
|
|
259
|
+
const merged = { ...current, ...data, recipe: mergedRecipe, };
|
|
260
|
+
await this.client.put(`/public/api/projects/${enc}/recipes/${rnEnc}`, merged);
|
|
261
|
+
}
|
|
262
|
+
/** Delete a recipe. */
|
|
263
|
+
async delete(recipeName, projectKey) {
|
|
264
|
+
const enc = this.enc(projectKey);
|
|
265
|
+
const rnEnc = encodeURIComponent(recipeName);
|
|
266
|
+
await this.client.del(`/public/api/projects/${enc}/recipes/${rnEnc}`);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Download a recipe definition as a JSON file.
|
|
270
|
+
* Returns the path to the written file.
|
|
271
|
+
*/
|
|
272
|
+
async download(recipeName, opts) {
|
|
273
|
+
const enc = this.enc(opts?.projectKey);
|
|
274
|
+
const rnEnc = encodeURIComponent(recipeName);
|
|
275
|
+
const recipe = await this.client.get(`/public/api/projects/${enc}/recipes/${rnEnc}`);
|
|
276
|
+
const safeRecipeName = sanitizeFileName(recipeName, "recipe");
|
|
277
|
+
const filePath = opts?.outputPath ?? resolve(process.cwd(), `${safeRecipeName}.json`);
|
|
278
|
+
await writeFile(filePath, JSON.stringify(recipe, null, 2), "utf-8");
|
|
279
|
+
return filePath;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ScenarioDetails, ScenarioStatus, ScenarioSummary } from "../schemas.js";
|
|
2
|
+
import { BaseResource } from "./base.js";
|
|
3
|
+
export declare class ScenariosResource extends BaseResource {
|
|
4
|
+
/** List all scenarios in a project. */
|
|
5
|
+
list(projectKey?: string): Promise<ScenarioSummary[]>;
|
|
6
|
+
/** Get full scenario details. */
|
|
7
|
+
get(scenarioId: string, opts?: {
|
|
8
|
+
projectKey?: string;
|
|
9
|
+
}): Promise<ScenarioDetails>;
|
|
10
|
+
/** Create a new scenario. */
|
|
11
|
+
create(scenarioId: string, name: string, opts?: {
|
|
12
|
+
scenarioType?: "step_based" | "custom_python";
|
|
13
|
+
data?: Record<string, unknown>;
|
|
14
|
+
projectKey?: string;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
/** Trigger a scenario run. */
|
|
17
|
+
run(scenarioId: string, projectKey?: string): Promise<{
|
|
18
|
+
runId: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Get the light/status view of a scenario. */
|
|
21
|
+
status(scenarioId: string, projectKey?: string): Promise<ScenarioStatus>;
|
|
22
|
+
/** Merge-update a scenario's definition. */
|
|
23
|
+
update(scenarioId: string, data: Record<string, unknown>, projectKey?: string): Promise<void>;
|
|
24
|
+
/** Delete a scenario. */
|
|
25
|
+
delete(scenarioId: string, projectKey?: string): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, } from "../schemas.js";
|
|
2
|
+
import { deepMerge, } from "../utils/deep-merge.js";
|
|
3
|
+
import { BaseResource, } from "./base.js";
|
|
4
|
+
export class ScenariosResource extends BaseResource {
|
|
5
|
+
/** List all scenarios in a project. */
|
|
6
|
+
async list(projectKey) {
|
|
7
|
+
const raw = await this.client.get(`/public/api/projects/${this.enc(projectKey)}/scenarios/`);
|
|
8
|
+
return this.client.safeParse(ScenarioSummaryArraySchema, raw, "scenarios.list");
|
|
9
|
+
}
|
|
10
|
+
/** Get full scenario details. */
|
|
11
|
+
async get(scenarioId, opts) {
|
|
12
|
+
const scEnc = encodeURIComponent(scenarioId);
|
|
13
|
+
const raw = await this.client.get(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/${scEnc}/`);
|
|
14
|
+
return this.client.safeParse(ScenarioDetailsSchema, raw, "scenarios.get");
|
|
15
|
+
}
|
|
16
|
+
/** Create a new scenario. */
|
|
17
|
+
async create(scenarioId, name, opts) {
|
|
18
|
+
const pk = this.resolveProjectKey(opts?.projectKey);
|
|
19
|
+
const scenarioType = opts?.scenarioType ?? "step_based";
|
|
20
|
+
const body = {
|
|
21
|
+
id: scenarioId,
|
|
22
|
+
name,
|
|
23
|
+
projectKey: pk,
|
|
24
|
+
type: scenarioType,
|
|
25
|
+
params: scenarioType === "step_based" ? { steps: [], triggers: [], reporters: [], } : {},
|
|
26
|
+
...opts?.data,
|
|
27
|
+
};
|
|
28
|
+
await this.client.post(`/public/api/projects/${this.enc(opts?.projectKey)}/scenarios/`, body);
|
|
29
|
+
}
|
|
30
|
+
/** Trigger a scenario run. */
|
|
31
|
+
async run(scenarioId, projectKey) {
|
|
32
|
+
const scEnc = encodeURIComponent(scenarioId);
|
|
33
|
+
const result = await this.client.post(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/run/`, {});
|
|
34
|
+
return {
|
|
35
|
+
runId: result.id ?? result.runId ?? "unknown",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Get the light/status view of a scenario. */
|
|
39
|
+
async status(scenarioId, projectKey) {
|
|
40
|
+
const scEnc = encodeURIComponent(scenarioId);
|
|
41
|
+
const raw = await this.client.get(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/light/`);
|
|
42
|
+
return this.client.safeParse(ScenarioStatusSchema, raw, "scenarios.status");
|
|
43
|
+
}
|
|
44
|
+
/** Merge-update a scenario's definition. */
|
|
45
|
+
async update(scenarioId, data, projectKey) {
|
|
46
|
+
const scEnc = encodeURIComponent(scenarioId);
|
|
47
|
+
const pkEnc = this.enc(projectKey);
|
|
48
|
+
const current = await this.client.get(`/public/api/projects/${pkEnc}/scenarios/${scEnc}/`);
|
|
49
|
+
const merged = deepMerge(current, data);
|
|
50
|
+
await this.client.put(`/public/api/projects/${pkEnc}/scenarios/${scEnc}/`, merged);
|
|
51
|
+
}
|
|
52
|
+
/** Delete a scenario. */
|
|
53
|
+
async delete(scenarioId, projectKey) {
|
|
54
|
+
const scEnc = encodeURIComponent(scenarioId);
|
|
55
|
+
await this.client.del(`/public/api/projects/${this.enc(projectKey)}/scenarios/${scEnc}/`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SqlQueryResponse, SqlQueryResult } from "../schemas.js";
|
|
2
|
+
import { BaseResource } from "./base.js";
|
|
3
|
+
export declare class SqlResource extends BaseResource {
|
|
4
|
+
/**
|
|
5
|
+
* Start a SQL query and return the queryId + schema.
|
|
6
|
+
* Specify either `connection` (run against a DB connection)
|
|
7
|
+
* or `datasetFullName` (run against a dataset's connection).
|
|
8
|
+
*/
|
|
9
|
+
startQuery(opts: {
|
|
10
|
+
query: string;
|
|
11
|
+
connection?: string;
|
|
12
|
+
datasetFullName?: string;
|
|
13
|
+
database?: string;
|
|
14
|
+
preQueries?: string[];
|
|
15
|
+
postQueries?: string[];
|
|
16
|
+
type?: string;
|
|
17
|
+
}): Promise<SqlQueryResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Stream results of a started query as parsed JSON (array of arrays).
|
|
20
|
+
*/
|
|
21
|
+
streamResults(queryId: string): Promise<unknown[][]>;
|
|
22
|
+
/**
|
|
23
|
+
* Verify that a query finished successfully server-side.
|
|
24
|
+
* Throws on failure.
|
|
25
|
+
*/
|
|
26
|
+
finishStreaming(queryId: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Execute a SQL query end-to-end: start, stream all rows, verify, return combined result.
|
|
29
|
+
* This is the primary method most callers want.
|
|
30
|
+
*/
|
|
31
|
+
query(opts: {
|
|
32
|
+
query: string;
|
|
33
|
+
connection?: string;
|
|
34
|
+
datasetFullName?: string;
|
|
35
|
+
database?: string;
|
|
36
|
+
preQueries?: string[];
|
|
37
|
+
postQueries?: string[];
|
|
38
|
+
type?: string;
|
|
39
|
+
}): Promise<SqlQueryResponse>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BaseResource, } from "./base.js";
|
|
2
|
+
export class SqlResource extends BaseResource {
|
|
3
|
+
/**
|
|
4
|
+
* Start a SQL query and return the queryId + schema.
|
|
5
|
+
* Specify either `connection` (run against a DB connection)
|
|
6
|
+
* or `datasetFullName` (run against a dataset's connection).
|
|
7
|
+
*/
|
|
8
|
+
async startQuery(opts) {
|
|
9
|
+
return this.client.post(`/public/api/sql/queries`, opts);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Stream results of a started query as parsed JSON (array of arrays).
|
|
13
|
+
*/
|
|
14
|
+
async streamResults(queryId) {
|
|
15
|
+
const id = encodeURIComponent(queryId);
|
|
16
|
+
const text = await this.client.getText(`/public/api/sql/queries/${id}/stream?format=json`);
|
|
17
|
+
return JSON.parse(text);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Verify that a query finished successfully server-side.
|
|
21
|
+
* Throws on failure.
|
|
22
|
+
*/
|
|
23
|
+
async finishStreaming(queryId) {
|
|
24
|
+
const id = encodeURIComponent(queryId);
|
|
25
|
+
const text = await this.client.getText(`/public/api/sql/queries/${id}/finish-streaming`);
|
|
26
|
+
if (text.length > 0) {
|
|
27
|
+
throw new Error(`SQL query ${queryId} failed: ${text}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Execute a SQL query end-to-end: start, stream all rows, verify, return combined result.
|
|
32
|
+
* This is the primary method most callers want.
|
|
33
|
+
*/
|
|
34
|
+
async query(opts) {
|
|
35
|
+
const { queryId, schema, } = await this.startQuery(opts);
|
|
36
|
+
const rows = await this.streamResults(queryId);
|
|
37
|
+
await this.finishStreaming(queryId);
|
|
38
|
+
return { queryId, schema, rows, };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProjectVariables } from "../schemas.js";
|
|
2
|
+
import { BaseResource } from "./base.js";
|
|
3
|
+
export declare class VariablesResource extends BaseResource {
|
|
4
|
+
get(projectKey?: string): Promise<ProjectVariables>;
|
|
5
|
+
set(opts: {
|
|
6
|
+
standard?: Record<string, unknown>;
|
|
7
|
+
local?: Record<string, unknown>;
|
|
8
|
+
projectKey?: string;
|
|
9
|
+
}): Promise<ProjectVariables>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ProjectVariablesSchema, } from "../schemas.js";
|
|
2
|
+
import { BaseResource, } from "./base.js";
|
|
3
|
+
export class VariablesResource extends BaseResource {
|
|
4
|
+
async get(projectKey) {
|
|
5
|
+
const enc = this.enc(projectKey);
|
|
6
|
+
const raw = await this.client.get(`/public/api/projects/${enc}/variables/`);
|
|
7
|
+
return this.client.safeParse(ProjectVariablesSchema, raw, "variables.get");
|
|
8
|
+
}
|
|
9
|
+
async set(opts) {
|
|
10
|
+
if (opts.standard === undefined && opts.local === undefined) {
|
|
11
|
+
throw new Error("At least one of standard or local must be provided");
|
|
12
|
+
}
|
|
13
|
+
const existing = await this.get(opts.projectKey);
|
|
14
|
+
const merged = {
|
|
15
|
+
standard: { ...existing.standard, ...opts.standard, },
|
|
16
|
+
local: { ...existing.local, ...opts.local, },
|
|
17
|
+
};
|
|
18
|
+
const enc = this.enc(opts.projectKey);
|
|
19
|
+
await this.client.putVoid(`/public/api/projects/${enc}/variables/`, merged);
|
|
20
|
+
return merged;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export all schemas, types, and validation helpers from the types package.
|
|
3
|
+
* The types package (packages/types/) owns the TypeBox schema definitions.
|
|
4
|
+
* SDK consumers get everything through this re-export.
|
|
5
|
+
*/
|
|
6
|
+
export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
|
|
7
|
+
export type { BuildMode, CodeEnvDetails, CodeEnvSummary, ConnectionSummary, DatasetCreateOptions, DatasetDetails, DatasetSchema, DatasetSummary, FlowMapOptions, FolderDetails, FolderItem, FolderSummary, JobSummary, JobWaitResult, JupyterCell, JupyterNotebookContent, JupyterNotebookSummary, NotebookSession, ProjectDetails, ProjectMetadata, ProjectSummary, ProjectVariables, RecipeCreateOptions, RecipeCreateResult, RecipeDetails, RecipeSummary, SafeParseResult, ScenarioDetails, ScenarioStatus, ScenarioSummary, SqlNotebookCell, SqlNotebookContent, SqlNotebookSummary, SqlQueryResponse, SqlQueryResult, SqlQuerySchema, } from "../packages/types/src/index.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-export all schemas, types, and validation helpers from the types package.
|
|
3
|
+
* The types package (packages/types/) owns the TypeBox schema definitions.
|
|
4
|
+
* SDK consumers get everything through this re-export.
|
|
5
|
+
*/
|
|
6
|
+
export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "../packages/types/src/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deepMerge<T extends Record<string, unknown>>(base: T, patch: Record<string, unknown>): T;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function deepMerge(base, patch) {
|
|
5
|
+
const out = { ...base, };
|
|
6
|
+
for (const [key, patchValue,] of Object.entries(patch)) {
|
|
7
|
+
const baseValue = out[key];
|
|
8
|
+
if (isPlainObject(baseValue) && isPlainObject(patchValue)) {
|
|
9
|
+
out[key] = deepMerge(baseValue, patchValue);
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
out[key] = patchValue;
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type FlowNodeKind = "dataset" | "recipe" | "folder" | "other";
|
|
2
|
+
export type FlowEdgeRelation = "reads" | "writes" | "depends_on" | "unknown";
|
|
3
|
+
export interface NormalizedFlowNode {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: FlowNodeKind;
|
|
6
|
+
name?: string;
|
|
7
|
+
subtype?: string;
|
|
8
|
+
connection?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NormalizedFlowEdge {
|
|
11
|
+
from: string;
|
|
12
|
+
to: string;
|
|
13
|
+
relation: FlowEdgeRelation;
|
|
14
|
+
}
|
|
15
|
+
export interface NormalizedFlowMap {
|
|
16
|
+
projectKey: string;
|
|
17
|
+
nodes: NormalizedFlowNode[];
|
|
18
|
+
edges: NormalizedFlowEdge[];
|
|
19
|
+
stats: {
|
|
20
|
+
nodeCount: number;
|
|
21
|
+
edgeCount: number;
|
|
22
|
+
datasets: number;
|
|
23
|
+
recipes: number;
|
|
24
|
+
roots: number;
|
|
25
|
+
leaves: number;
|
|
26
|
+
};
|
|
27
|
+
roots: string[];
|
|
28
|
+
leaves: string[];
|
|
29
|
+
warnings: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface NormalizeFlowGraphOptions {
|
|
32
|
+
folderNamesById?: Record<string, string>;
|
|
33
|
+
allDatasetNames?: string[];
|
|
34
|
+
allRecipeNames?: string[];
|
|
35
|
+
allFolderIds?: string[];
|
|
36
|
+
}
|
|
37
|
+
export declare function normalizeFlowGraph(raw: unknown, projectKey: string, options?: NormalizeFlowGraphOptions): NormalizedFlowMap;
|