@lightdash/warehouses 0.1992.1 → 0.1992.2
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.
|
@@ -70,5 +70,6 @@ export declare class SnowflakeWarehouseClient extends WarehouseBaseClient<Create
|
|
|
70
70
|
table: any;
|
|
71
71
|
}[]>;
|
|
72
72
|
getFields(tableName: string, schema?: string, database?: string, tags?: Record<string, string>): Promise<WarehouseCatalog>;
|
|
73
|
+
private formatCustomErrorMessage;
|
|
73
74
|
parseError(error: SnowflakeError, query?: string): WarehouseQueryError;
|
|
74
75
|
}
|
|
@@ -613,11 +613,44 @@ class SnowflakeWarehouseClient extends WarehouseBaseClient_1.default {
|
|
|
613
613
|
const { rows } = await this.runQuery(query, tags, undefined, values);
|
|
614
614
|
return this.parseWarehouseCatalog(rows, exports.mapFieldType);
|
|
615
615
|
}
|
|
616
|
+
/*
|
|
617
|
+
* This function is used to format the error message for the user.
|
|
618
|
+
* It is used to replace the {snowflakeTable} and {snowflakeSchema} with the actual table and schema names.
|
|
619
|
+
* Sample custom template: You don't have access to the {snowflakeTable} table. Please go to '{snowflakeSchema}' and request access
|
|
620
|
+
*/
|
|
621
|
+
formatCustomErrorMessage(originalMessage, customTemplate) {
|
|
622
|
+
let formattedMessage = customTemplate;
|
|
623
|
+
// Extract table information from the original error message
|
|
624
|
+
// Pattern matches: Object 'DATABASE.SCHEMA.TABLE' does not exist or not authorized
|
|
625
|
+
const tableMatch = originalMessage.match(/Object '([^']+)' does not exist or not authorized/i);
|
|
626
|
+
if (tableMatch) {
|
|
627
|
+
const fullTableName = tableMatch[1];
|
|
628
|
+
const parts = fullTableName.split('.');
|
|
629
|
+
if (parts.length >= 3) {
|
|
630
|
+
const snowflakeTable = parts[parts.length - 1]; // Last part is table name
|
|
631
|
+
const snowflakeSchema = parts[parts.length - 2]; // Second to last is schema
|
|
632
|
+
// Replace variables in the custom message
|
|
633
|
+
formattedMessage = formattedMessage
|
|
634
|
+
.replace(/\{snowflakeTable\}/g, snowflakeTable)
|
|
635
|
+
.replace(/\{snowflakeSchema\}/g, snowflakeSchema);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return formattedMessage;
|
|
639
|
+
}
|
|
616
640
|
parseError(error, query = '') {
|
|
617
641
|
// if the error has no code or data, return a generic error
|
|
618
642
|
if (!error?.code && !error.data) {
|
|
619
643
|
return new common_1.WarehouseQueryError(error?.message || 'Unknown error');
|
|
620
644
|
}
|
|
645
|
+
const originalMessage = error?.message || 'Unknown error';
|
|
646
|
+
// Check for unauthorized access errors and use custom message if configured
|
|
647
|
+
if (originalMessage.includes('does not exist or not authorized')) {
|
|
648
|
+
const customErrorMessage = process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE;
|
|
649
|
+
if (customErrorMessage) {
|
|
650
|
+
const formattedMessage = this.formatCustomErrorMessage(originalMessage, customErrorMessage);
|
|
651
|
+
return new common_1.WarehouseQueryError(formattedMessage);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
621
654
|
// pull error type from data object
|
|
622
655
|
const errorType = error.data?.type || error.code;
|
|
623
656
|
switch (errorType) {
|
|
@@ -652,7 +685,7 @@ class SnowflakeWarehouseClient extends WarehouseBaseClient_1.default {
|
|
|
652
685
|
break;
|
|
653
686
|
}
|
|
654
687
|
// otherwise return a generic error
|
|
655
|
-
return new common_1.WarehouseQueryError(
|
|
688
|
+
return new common_1.WarehouseQueryError(originalMessage);
|
|
656
689
|
}
|
|
657
690
|
}
|
|
658
691
|
exports.SnowflakeWarehouseClient = SnowflakeWarehouseClient;
|
|
@@ -65,3 +65,82 @@ describe('SnowflakeTypeParsing', () => {
|
|
|
65
65
|
expect((0, SnowflakeWarehouseClient_1.mapFieldType)('VARCHAR(12)')).toEqual(common_1.DimensionType.STRING);
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
|
+
describe('SnowflakeErrorParsing', () => {
|
|
69
|
+
let warehouse;
|
|
70
|
+
const originalEnv = process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE;
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
warehouse = new SnowflakeWarehouseClient_1.SnowflakeWarehouseClient(SnowflakeWarehouseClient_mock_1.credentials);
|
|
73
|
+
});
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
// Restore original environment variable
|
|
76
|
+
if (originalEnv) {
|
|
77
|
+
process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE = originalEnv;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
delete process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
it('should return custom error message when SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE is set', () => {
|
|
84
|
+
// Set environment variable
|
|
85
|
+
process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE =
|
|
86
|
+
"You don't have access to the {snowflakeTable} table. Please go to 'analytics_{snowflakeSchema}' in sailpoint and request access";
|
|
87
|
+
const error = {
|
|
88
|
+
message: "SQL compilation error: Object 'SNOWFLAKE_DATABASE_STAGING.JAFFLE.EVENTS' does not exist or not authorized.",
|
|
89
|
+
code: 'COMPILATION',
|
|
90
|
+
data: { type: 'COMPILATION' },
|
|
91
|
+
};
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
const result = warehouse.parseError(error);
|
|
94
|
+
expect(result.message).toBe("You don't have access to the EVENTS table. Please go to 'analytics_JAFFLE' in sailpoint and request access");
|
|
95
|
+
});
|
|
96
|
+
it('should return original error message when SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE is not set', () => {
|
|
97
|
+
// Make sure environment variable is not set
|
|
98
|
+
delete process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE;
|
|
99
|
+
const error = {
|
|
100
|
+
message: "SQL compilation error: Object 'SNOWFLAKE_DATABASE_STAGING.JAFFLE.EVENTS' does not exist or not authorized.",
|
|
101
|
+
code: 'COMPILATION',
|
|
102
|
+
data: { type: 'COMPILATION' },
|
|
103
|
+
};
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
const result = warehouse.parseError(error);
|
|
106
|
+
expect(result.message).toBe("SQL compilation error: Object 'SNOWFLAKE_DATABASE_STAGING.JAFFLE.EVENTS' does not exist or not authorized.");
|
|
107
|
+
});
|
|
108
|
+
it('should return original error message for non-unauthorized errors', () => {
|
|
109
|
+
// Set environment variable
|
|
110
|
+
process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE =
|
|
111
|
+
"You don't have access to the {snowflakeTable} table. Please go to 'analytics_{snowflakeSchema}' in sailpoint and request access";
|
|
112
|
+
const error = {
|
|
113
|
+
message: 'Some other SQL error',
|
|
114
|
+
code: 'COMPILATION',
|
|
115
|
+
data: { type: 'COMPILATION' },
|
|
116
|
+
};
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
+
const result = warehouse.parseError(error);
|
|
119
|
+
expect(result.message).toBe('Some other SQL error');
|
|
120
|
+
});
|
|
121
|
+
it('should handle table names with different formats', () => {
|
|
122
|
+
process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE =
|
|
123
|
+
'Access denied for {snowflakeTable} in {snowflakeSchema}';
|
|
124
|
+
const error = {
|
|
125
|
+
message: "Object 'DB.MY_SCHEMA.MY_TABLE' does not exist or not authorized.",
|
|
126
|
+
code: 'COMPILATION',
|
|
127
|
+
data: { type: 'COMPILATION' },
|
|
128
|
+
};
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
+
const result = warehouse.parseError(error);
|
|
131
|
+
expect(result.message).toBe('Access denied for MY_TABLE in MY_SCHEMA');
|
|
132
|
+
});
|
|
133
|
+
it('should handle errors without table information gracefully', () => {
|
|
134
|
+
process.env.SNOWFLAKE_UNAUTHORIZED_ERROR_MESSAGE =
|
|
135
|
+
"You don't have access to the {snowflakeTable} table. Please go to 'analytics_{snowflakeSchema}' in sailpoint and request access";
|
|
136
|
+
const error = {
|
|
137
|
+
message: "Object 'INCOMPLETE_TABLE_NAME' does not exist or not authorized.",
|
|
138
|
+
code: 'COMPILATION',
|
|
139
|
+
data: { type: 'COMPILATION' },
|
|
140
|
+
};
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
+
const result = warehouse.parseError(error);
|
|
143
|
+
// Should still use custom message but without variable replacement
|
|
144
|
+
expect(result.message).toBe("You don't have access to the {snowflakeTable} table. Please go to 'analytics_{snowflakeSchema}' in sailpoint and request access");
|
|
145
|
+
});
|
|
146
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/warehouses",
|
|
3
|
-
"version": "0.1992.
|
|
3
|
+
"version": "0.1992.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"snowflake-sdk": "~2.1.3",
|
|
17
17
|
"ssh2": "^1.14.0",
|
|
18
18
|
"trino-client": "0.2.6",
|
|
19
|
-
"@lightdash/common": "0.1992.
|
|
19
|
+
"@lightdash/common": "0.1992.2"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/pg": "^8.11.10",
|