@lightdash/warehouses 0.1992.1 → 0.1992.3

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(error?.message || 'Unknown error');
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.1",
3
+ "version": "0.1992.3",
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.1"
19
+ "@lightdash/common": "0.1992.3"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/pg": "^8.11.10",