@lightdash/warehouses 2.520.0 → 2.522.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/dist/index.d.ts CHANGED
@@ -16,5 +16,4 @@ export * from './warehouseClients/redshiftIamCredentials';
16
16
  export * from './warehouseClients/SnowflakeWarehouseClient';
17
17
  export * from './warehouseClients/TrinoWarehouseClient';
18
18
  export * from './warehouseSqlBuilderFromType';
19
- export * from './agentAccess/BigqueryAgentAccessProbe';
20
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,qCAAqC,CAAC;AACpD,cAAc,kCAAkC,CAAC;AACjD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,YAAY,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AACvF,cAAc,2DAA2D,CAAC;AAC1E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,SAAS,CAAC;AACxB,cAAc,qCAAqC,CAAC;AACpD,cAAc,kCAAkC,CAAC;AACjD,cAAc,0CAA0C,CAAC;AACzD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,8CAA8C,CAAC;AAC7D,cAAc,0CAA0C,CAAC;AACzD,cAAc,yCAAyC,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,YAAY,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AACvF,cAAc,2DAA2D,CAAC;AAC1E,cAAc,4CAA4C,CAAC;AAC3D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,yCAAyC,CAAC;AACxD,cAAc,+BAA+B,CAAC"}
package/dist/index.js CHANGED
@@ -33,4 +33,3 @@ __exportStar(require("./warehouseClients/redshiftIamCredentials"), exports);
33
33
  __exportStar(require("./warehouseClients/SnowflakeWarehouseClient"), exports);
34
34
  __exportStar(require("./warehouseClients/TrinoWarehouseClient"), exports);
35
35
  __exportStar(require("./warehouseSqlBuilderFromType"), exports);
36
- __exportStar(require("./agentAccess/BigqueryAgentAccessProbe"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/warehouses",
3
- "version": "2.520.0",
3
+ "version": "2.522.0",
4
4
  "description": "Warehouse connectors for Lightdash",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "@databricks/sql": "1.13.0",
22
22
  "@duckdb/node-api": "1.5.2-r.2",
23
23
  "@google-cloud/bigquery": "8.3.1",
24
- "@lightdash/common": "2.520.0",
24
+ "@lightdash/common": "2.522.0",
25
25
  "node-fetch": "2.7.0",
26
26
  "pg": "8.13.1",
27
27
  "pg-cursor": "2.10.0",
@@ -1,31 +0,0 @@
1
- import { type AgentAccessDataset, type AgentAccessReport, type AgentTableStatus } from '@lightdash/common';
2
- import { BigqueryWarehouseClient } from '../warehouseClients/BigqueryWarehouseClient';
3
- export type AgentAccessInventoryTable = AgentAccessDataset & {
4
- name: string;
5
- location: string;
6
- };
7
- type FailureReason = NonNullable<AgentAccessReport['failureReason']>;
8
- export declare class AgentAccessProbeError extends Error {
9
- readonly reason: FailureReason;
10
- constructor(reason: FailureReason);
11
- }
12
- export declare const classifyBigqueryAccessError: (error: unknown, phase?: 'identity' | 'tables') => {
13
- kind: 'fatal';
14
- reason: FailureReason;
15
- } | Exclude<AgentTableStatus, {
16
- kind: 'readable';
17
- } | {
18
- kind: 'not_checked';
19
- }>;
20
- export declare class BigqueryAgentAccessProbe {
21
- private readonly warehouse;
22
- private accessToken;
23
- constructor(warehouse: BigqueryWarehouseClient);
24
- private withDeadline;
25
- private request;
26
- principal(deadline: number): Promise<string>;
27
- listTables(dataset: AgentAccessDataset, deadline: number): Promise<AgentAccessInventoryTable[]>;
28
- probe(table: AgentAccessInventoryTable, deadline: number): Promise<AgentTableStatus>;
29
- }
30
- export {};
31
- //# sourceMappingURL=BigqueryAgentAccessProbe.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BigqueryAgentAccessProbe.d.ts","sourceRoot":"","sources":["../../src/agentAccess/BigqueryAgentAccessProbe.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AAEtF,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,GAAG;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,aAAa,GAAG,WAAW,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAAC;AAErE,qBAAa,qBAAsB,SAAQ,KAAK;aAChB,MAAM,EAAE,aAAa;IAAjD,YAA4B,MAAM,EAAE,aAAa,EAGhD;CACJ;AAOD,eAAO,MAAM,2BAA2B,UAC7B,OAAO,UACP,UAAU,GAAG,QAAQ,KAE1B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACxC,OAAO,CACH,gBAAgB,EAChB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CA8EvD,CAAC;AAKF,qBAAa,wBAAwB;IAGrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,OAAO,CAAC,WAAW,CAAgC;IAEnD,YAA6B,SAAS,EAAE,uBAAuB,EAAI;YAErD,YAAY;YAuBZ,OAAO;IA0Cf,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BjD;IAEK,UAAU,CACZ,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAiCtC;IAEK,KAAK,CACP,KAAK,EAAE,yBAAyB,EAChC,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAyC3B;CACJ"}
@@ -1,210 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BigqueryAgentAccessProbe = exports.classifyBigqueryAccessError = exports.AgentAccessProbeError = void 0;
4
- class AgentAccessProbeError extends Error {
5
- constructor(reason) {
6
- super(reason);
7
- this.reason = reason;
8
- this.name = 'AgentAccessProbeError';
9
- }
10
- }
11
- exports.AgentAccessProbeError = AgentAccessProbeError;
12
- const record = (value) => typeof value === 'object' && value !== null
13
- ? value
14
- : {};
15
- const classifyBigqueryAccessError = (error, phase = 'tables') => {
16
- if (error instanceof AgentAccessProbeError)
17
- return { kind: 'fatal', reason: error.reason };
18
- const root = record(error);
19
- const response = record(root.response);
20
- const data = record(response.data);
21
- const detail = record(data.error);
22
- const errors = root.errors ?? detail.errors;
23
- const entries = Array.isArray(errors) ? errors.map(record) : [];
24
- const reasons = entries.map((entry) => entry.reason);
25
- const code = detail.code ?? response.status ?? root.code;
26
- const message = [
27
- root.message,
28
- detail.message,
29
- ...entries.map((entry) => entry.message),
30
- ]
31
- .filter((value) => typeof value === 'string')
32
- .join(' ');
33
- if (code === 401 ||
34
- reasons.some((reason) => ['authError', 'invalidCredentials', 'invalid_grant'].includes(String(reason))) ||
35
- data.error === 'invalid_grant')
36
- return { kind: 'fatal', reason: 'invalid_credentials' };
37
- if (reasons.some((reason) => [
38
- 'rateLimitExceeded',
39
- 'jobRateLimitExceeded',
40
- 'quotaExceeded',
41
- 'resourcesExceeded',
42
- ].includes(String(reason))) ||
43
- code === 429)
44
- return { kind: 'error', reason: 'quota' };
45
- if (message.includes('bigquery.jobs.create'))
46
- return { kind: 'fatal', reason: 'job_permission_denied' };
47
- if (reasons.includes('accessDenied'))
48
- return { kind: 'blocked', reason: 'access_denied' };
49
- if (reasons.includes('notFound') || code === 404)
50
- return { kind: 'error', reason: 'not_found' };
51
- if (['ETIMEDOUT', 'ESOCKETTIMEDOUT', 'ABORT_ERR'].includes(String(code)) ||
52
- root.name === 'AbortError' ||
53
- root.name === 'TimeoutError')
54
- return { kind: 'error', reason: 'timeout' };
55
- if (reasons.some((reason) => ['invalidQuery', 'invalid', 'notImplemented'].includes(String(reason))))
56
- return { kind: 'error', reason: 'unsupported' };
57
- if ((typeof code === 'number' && code >= 500) ||
58
- ['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'EAI_AGAIN'].includes(String(code)) ||
59
- reasons.includes('backendError'))
60
- return { kind: 'error', reason: 'unavailable' };
61
- if (code === 403)
62
- return { kind: 'fatal', reason: 'warehouse_denied' };
63
- if (phase === 'identity' &&
64
- root.response == null &&
65
- typeof code !== 'number' &&
66
- entries.length === 0)
67
- return { kind: 'fatal', reason: 'invalid_credentials' };
68
- return { kind: 'error', reason: 'unknown' };
69
- };
70
- exports.classifyBigqueryAccessError = classifyBigqueryAccessError;
71
- const quoteTable = (table) => `\`${[table.database, table.schema, table.name].map((part) => part.replaceAll('\\', '\\\\').replaceAll('`', '\\`')).join('.')}\``;
72
- class BigqueryAgentAccessProbe {
73
- constructor(warehouse) {
74
- this.warehouse = warehouse;
75
- this.accessToken = null;
76
- }
77
- async withDeadline(operation, deadline) {
78
- const timeout = Math.min(5000, deadline - Date.now());
79
- if (timeout <= 0)
80
- throw new AgentAccessProbeError('timeout');
81
- const controller = new AbortController();
82
- let timer;
83
- try {
84
- return await Promise.race([
85
- operation(controller.signal),
86
- new Promise((_, reject) => {
87
- timer = setTimeout(() => {
88
- controller.abort();
89
- reject(new AgentAccessProbeError('timeout'));
90
- }, timeout);
91
- }),
92
- ]);
93
- }
94
- finally {
95
- clearTimeout(timer);
96
- }
97
- }
98
- async request(path, method, data, deadline) {
99
- const requestDeadline = Math.min(Date.now() + 5000, deadline);
100
- return this.withDeadline(async (signal) => {
101
- this.accessToken ?? (this.accessToken = this.withDeadline(async () => {
102
- const token = await this.warehouse.client.authClient.getAccessToken();
103
- if (!token)
104
- throw new AgentAccessProbeError('invalid_credentials');
105
- return token;
106
- }, requestDeadline));
107
- const token = await this.accessToken;
108
- signal.throwIfAborted();
109
- if (Date.now() >= requestDeadline)
110
- throw new AgentAccessProbeError('timeout');
111
- const response = await fetch(`${this.warehouse.client.baseUrl}/${path}`, {
112
- method,
113
- headers: {
114
- Authorization: `Bearer ${token}`,
115
- 'Content-Type': 'application/json',
116
- },
117
- ...(data === null ? {} : { body: JSON.stringify(data) }),
118
- signal,
119
- });
120
- if (!response.ok) {
121
- const body = await response.json().catch(() => null);
122
- throw Object.assign(new Error('The warehouse rejected the access check.'), { response: { status: response.status, data: body } });
123
- }
124
- return response.json();
125
- }, requestDeadline);
126
- }
127
- async principal(deadline) {
128
- const project = this.warehouse.credentials.executionProject ||
129
- this.warehouse.credentials.project;
130
- const response = await this.request(`projects/${encodeURIComponent(project)}/queries`, 'POST', {
131
- query: 'SELECT SESSION_USER() AS principal',
132
- useLegacySql: false,
133
- useQueryCache: false,
134
- labels: { agent: 'true' },
135
- location: this.warehouse.credentials.location || undefined,
136
- timeoutMs: 4000,
137
- jobTimeoutMs: '5000',
138
- }, deadline);
139
- if (response.errors?.length)
140
- throw Object.assign(new Error('The warehouse rejected the identity check.'), { errors: response.errors });
141
- if (!response.jobComplete)
142
- throw new AgentAccessProbeError('timeout');
143
- const principal = response.rows?.[0]?.f?.[0]?.v;
144
- if (typeof principal !== 'string' || principal.length === 0)
145
- throw new AgentAccessProbeError('unknown');
146
- return principal;
147
- }
148
- async listTables(dataset, deadline) {
149
- const path = `projects/${encodeURIComponent(dataset.database)}/datasets/${encodeURIComponent(dataset.schema)}`;
150
- const metadata = await this.request(path, 'GET', null, deadline);
151
- const { location } = metadata;
152
- if (!location)
153
- throw new AgentAccessProbeError('baseline_failed');
154
- const tables = [];
155
- const listPage = async (pageToken) => {
156
- const query = new URLSearchParams({ maxResults: '1000' });
157
- if (pageToken !== null)
158
- query.set('pageToken', pageToken);
159
- const page = await this.request(`${path}/tables?${query}`, 'GET', null, deadline);
160
- for (const table of page.tables ?? []) {
161
- if (!table.tableReference?.tableId)
162
- throw new AgentAccessProbeError('baseline_failed');
163
- tables.push({
164
- ...dataset,
165
- name: table.tableReference.tableId,
166
- location,
167
- });
168
- }
169
- if (page.nextPageToken)
170
- await listPage(page.nextPageToken);
171
- };
172
- await listPage(null);
173
- return tables;
174
- }
175
- async probe(table, deadline) {
176
- try {
177
- const project = this.warehouse.credentials.executionProject ||
178
- this.warehouse.credentials.project;
179
- const result = await this.request(`projects/${encodeURIComponent(project)}/jobs`, 'POST', {
180
- jobReference: {
181
- projectId: project,
182
- location: table.location,
183
- },
184
- configuration: {
185
- dryRun: true,
186
- labels: { agent: 'true' },
187
- jobTimeoutMs: '5000',
188
- query: {
189
- query: `SELECT * FROM ${quoteTable(table)}`,
190
- useLegacySql: false,
191
- useQueryCache: false,
192
- },
193
- },
194
- }, deadline);
195
- if (result.status?.errorResult)
196
- throw Object.assign(new Error('The warehouse rejected the access check.'), { errors: [result.status.errorResult] });
197
- return { kind: 'readable', reason: null };
198
- }
199
- catch (error) {
200
- const status = (0, exports.classifyBigqueryAccessError)(error);
201
- if (status.kind === 'fatal') {
202
- if (status.reason === 'timeout')
203
- return { kind: 'error', reason: 'timeout' };
204
- throw new AgentAccessProbeError(status.reason);
205
- }
206
- return status;
207
- }
208
- }
209
- }
210
- exports.BigqueryAgentAccessProbe = BigqueryAgentAccessProbe;