Skip to content

Commit 421d956

Browse files
committed
Snowflake v1
1 parent 236204c commit 421d956

8 files changed

Lines changed: 362 additions & 0 deletions

File tree

plugins/Snowflake/v1/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Overview
2+
3+
A simple data source for Snowflake that supports Snowflake SQL queries. Requires an OAuth connection.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"steps": [
3+
{
4+
"displayName": "API access",
5+
"dataStream": {
6+
"name": "sqlQuery",
7+
"config": {
8+
"query": "show databases"
9+
}
10+
},
11+
"success": "User credentials has Snowflake query permissions.",
12+
"error": "User does not have permission to access the Snowflake API (query 'SHOW DATABASES' failed).",
13+
"required": true
14+
},
15+
{
16+
"displayName": "Compute access",
17+
"dataStream": {
18+
"name": "sqlQuery",
19+
"config": {
20+
"query": "select 1/1",
21+
"errorOnEmptyResults": true
22+
}
23+
},
24+
"success": "User has access to warehouse.",
25+
"error": "User does not have access to a warehouse (query 'SELECT 1/1' failed). Check user's role is configured with a default warehouse and has warehouse permissions.",
26+
"required": true
27+
},
28+
{
29+
"displayName": "Database access",
30+
"dataStream": {
31+
"name": "sqlQuery",
32+
"config": {
33+
"query": "show databases",
34+
"errorOnEmptyResults": true
35+
}
36+
},
37+
"success": "User has access to at least one database.",
38+
"error": "User does not have permission to access any databases. Check user's default role or specify a role.",
39+
"required": false
40+
}
41+
]
42+
}
43+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
result = data.data.map( r => r.reduce((obj, value, i) => {
2+
const columnName = data.resultSetMetaData.rowType[i].name;
3+
obj[columnName] = value;
4+
return obj;
5+
}, {}));
6+
7+
// support value column for autoComplete queries
8+
if (context.config.valueColumn) {
9+
metadata = [
10+
{
11+
name: context.config.valueColumn,
12+
role: "value"
13+
}
14+
]
15+
} else {
16+
const typeMapping = {
17+
"text": "string",
18+
"fixed": "number",
19+
"real": "number",
20+
"varchar": "string",
21+
"date": "date",
22+
"timestamp": "date"
23+
};
24+
25+
metadata = data.resultSetMetaData.rowType.map( c => {
26+
return {
27+
name: c.name,
28+
shape: typeMapping[c.type] || "string"
29+
}
30+
});
31+
}
32+
33+
// used for validation queries
34+
if (context.config.errorOnEmptyResults === true && data.data.length === 0) {
35+
throw new Error("No results");
36+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"name": "sqlQuery",
3+
"displayName": "SQL Query",
4+
"baseDataSourceName": "httpRequestUnscoped",
5+
"config": {
6+
"httpMethod": "post",
7+
"paging": {
8+
"mode": "none"
9+
},
10+
"expandInnerObjects": true,
11+
"endpointPath": "/v2/statements/",
12+
"postBody": {
13+
"database": "{{typeof database !== 'undefined' ? database : undefined}}",
14+
"schema": "{{typeof schema !== 'undefined' ? schema : undefined}}",
15+
"statement": "{{query}}"
16+
},
17+
"postRequestScript": "sqlQuery-post.js",
18+
"getArgs": [],
19+
"headers": [],
20+
"valueColumn": "{{typeof valueColumn !== 'undefined' ? valueColumn : undefined}}",
21+
"errorOnEmptyResults": "{{typeof errorOnEmptyResults !== 'undefined' ? errorOnEmptyResults : undefined}}"
22+
23+
},
24+
"ui": [
25+
{
26+
"name": "database",
27+
"type": "autocomplete",
28+
"label": "Database",
29+
"validation": {
30+
"required": false
31+
},
32+
"isMulti": false,
33+
"allowCustomValues": true,
34+
"data": {
35+
"source": "dataStream",
36+
"dataStreamName": "sqlQuery",
37+
"dataSourceConfig": {
38+
"valueColumn": "name",
39+
"query": "show databases"
40+
}
41+
}
42+
},
43+
{
44+
"name": "schema",
45+
"type": "autocomplete",
46+
"label": "Schema",
47+
"validation": {
48+
"required": false
49+
},
50+
"isMulti": false,
51+
"allowCustomValues": true,
52+
"data": {
53+
"source": "dataStream",
54+
"dataStreamName": "sqlQuery",
55+
"dataSourceConfig": {
56+
"valueColumn": "name",
57+
"database": {
58+
"fieldName": "database",
59+
"required": true
60+
},
61+
"query": "show schemas"
62+
}
63+
}
64+
},
65+
{
66+
"help": "Enter a query using Snowflake SQL syntax. You can also use parameters like {{timeframe.start}}, e.g. event_time BETWEEN '{{timeframe.start}}' AND '{{timeframe.end}}'",
67+
"name": "query",
68+
"language": "sql",
69+
"label": "SQL query",
70+
"type": "code",
71+
"validation": {
72+
"required": true
73+
}
74+
}
75+
],
76+
"manualConfigApply": true,
77+
"supportsNoneTimeframe": true,
78+
"requiresParameterTimeframe": true,
79+
"defaultTimeframe": "none"
80+
}

plugins/Snowflake/v1/docs/setup.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Before you start
2+
3+
## Creating an OAuth integration in Snowflake
4+
5+
The Snowflake data source authenticates using OAuth.
6+
7+
Before configuring the data source you will need to register SquaredUp with your Snowflake account bby creating a custom integration.
8+
9+
Sample Snowflake commands for creating the integration are provided below.
10+
11+
For more information on creating a Snowflake integration see:
12+
https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake
13+
14+
15+
If your SquaredUp account is in the US region (default):
16+
17+
```
18+
CREATE SECURITY INTEGRATION oauth_squaredup
19+
TYPE = oauth
20+
OAUTH_CLIENT = custom
21+
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
22+
OAUTH_REDIRECT_URI = 'https://app.squaredup.com/settings/pluginsoauth2'
23+
COMMENT = 'Used by SquaredUp to connect to this Snowflake account'
24+
```
25+
26+
If your SquaredUp account is in the EU region (default):
27+
28+
```
29+
CREATE SECURITY INTEGRATION oauth_squaredup
30+
TYPE = oauth
31+
OAUTH_CLIENT = custom
32+
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
33+
OAUTH_REDIRECT_URI = 'https://eu.app.squaredup.com/settings/pluginsoauth2'
34+
COMMENT = 'Used by SquaredUp to connect to this Snowflake account'
35+
```
36+
37+
Once your integration is created, run:
38+
39+
```
40+
SELECT
41+
oauth:OAUTH_CLIENT_SECRET::STRING AS OAUTH_CLIENT_SECRET,
42+
oauth:OAUTH_CLIENT_ID::STRING AS OAUTH_CLIENT_ID
43+
FROM (SELECT PARSE_JSON(SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('oauth_squaredup')) AS oauth)
44+
45+
```
46+
47+
Use the values of the `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` columns in your configuration below.
48+
49+
50+
## Creating a read-only user
51+
52+
To connect to Snowflake you will need the credentials for a Snowflake user.
53+
54+
By default, it is NOT possible to connect via OAuth using an ACCOUNTADMIN role. Snowflake automatically adds privileged roles to the blocked role list used for OAuth authorization, see https://docs.snowflake.com/en/sql-reference/parameters#oauth-add-privileged-roles-to-blocked-list
55+
56+
We recommend a dedicated 'squaredup' user account that is assigned read only role. For more information on Snowflake users and roles, see https://docs.snowflake.com/en/user-guide/security-access-control-configure.
57+
58+
Ensure the user has a default role set, or specify the role when configuring the data source (see below). If the user does not have a default role and no role is specified, the connection will use the PUBLIC role, which typically does not have any permissions to databases.
59+
60+
61+
# Configuration
62+
63+
## Snowflake account identifier
64+
65+
Enter your Snowflake account identifier.
66+
67+
This can be found in the Snowflake portal under 'Your Username' > Account > Account Identifier.
68+
69+
The account identifier It is in the format <org_name>-<account_name>, e.g. ABCDEFG-XYZ12345
70+
71+
For example: `https://<your-opensearch-host>:9200`
72+
73+
Alternatively, run the following Snowflake query:
74+
75+
```
76+
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();
77+
```
78+
79+
## Snowflake OAuth client ID
80+
81+
The client ID for your Snowflake OAuth application.
82+
83+
Enter the `OAUTH_CLIENT_ID` value from the integration you created above.
84+
85+
## Snowflake OAuth client secret
86+
87+
The client secret for your Snowflake OAuth application.
88+
89+
Enter the `OAUTH_CLIENT_SECRET` value from the integration you created above.
90+
91+
## Role (optional)
92+
93+
Restrict OAuth connection to a specific role. If not specified, the user's default role is used.
94+
95+
If you have created a custom role for your database, for example a read-only role, enter its name here.
96+
97+
## Authorize
98+
99+
Click the Sign-in button to authorize SquaredUp to access Snowflake.

plugins/Snowflake/v1/icon.svg

Lines changed: 1 addition & 0 deletions
Loading

plugins/Snowflake/v1/metadata.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"name": "snowflake-preview",
3+
"displayName": "Snowflake (Preview)",
4+
"version": "1.0.0",
5+
"author": {
6+
"name": "SquaredUp Labs",
7+
"type": "labs"
8+
},
9+
"description": "Query data from Snowflake.",
10+
"category": "Database",
11+
"type": "hybrid",
12+
"schemaVersion": "2.0",
13+
"base": {
14+
"plugin": "WebAPI",
15+
"majorVersion": "1",
16+
"config": {
17+
"queryArgs": [],
18+
"headers": [],
19+
"oauth2TokenExtraArgs": [],
20+
"oauth2ClientSecret": "{{oauth2ClientSecret}}",
21+
"oauth2ClientSecretLocationDuringAuth": "header",
22+
"oauth2AuthUrl": "https://{{accountId}}.snowflakecomputing.com/oauth/authorize",
23+
"authMode": "oauth2",
24+
"oauth2GrantType": "authCode",
25+
"baseUrl": "https://{{accountId}}.snowflakecomputing.com/api",
26+
"oauth2TokenExtraHeaders": [
27+
{
28+
"value": "application/x-www-form-urlencoded",
29+
"key": "Content-Type"
30+
}
31+
],
32+
"oauth2ClientId": "{{oauth2ClientId}}",
33+
"oauth2TokenUrl": "https://{{accountId}}.snowflakecomputing.com/oauth/token-request",
34+
"oauth2AuthExtraArgs": [],
35+
"oauth2Scope": "refresh_token {{oauth2Role? 'session:role:' + oauth2Role : ''}}"
36+
}
37+
},
38+
"links": [
39+
{
40+
"category": "documentation",
41+
"url": "https://github.com/squaredup/plugins/blob/main/plugins/Snowflake/v1/docs/setup.md",
42+
"label": "Help adding this plugin"
43+
},
44+
{
45+
"category": "source",
46+
"url": "https://github.com/squaredup/plugins/tree/main/plugins/Snowflake/v1",
47+
"label": "Repository"
48+
}
49+
]
50+
}

plugins/Snowflake/v1/ui.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"type": "text",
4+
"name": "accountId",
5+
"label": "Snowflake account identifier",
6+
"help": "Enter your Snowflake account identifier. Find this in the portal under Your Username > Account > Account Identifier. It is in the format <org_name>-<account_name>, e.g. ABCDEFG-XYZ12345",
7+
"validation": {
8+
"required": true
9+
},
10+
"placeholder": "<org_name>-<account_name>, e.g. ABCDEFG-XYZ12345"
11+
},
12+
{
13+
"type": "text",
14+
"name": "oauth2ClientId",
15+
"label": "Snowflake OAuth client ID",
16+
"help": "The client ID for your Snowflake OAuth application. See documentation for details on how to set up an OAuth application in Snowflake and obtain the client ID.",
17+
"validation": {
18+
"required": true
19+
},
20+
"placeholder": "Enter your Snowflake OAuth client ID"
21+
},
22+
{
23+
"type": "password",
24+
"name": "oauth2ClientSecret",
25+
"label": "Snowflake OAuth secret",
26+
"help": "The client secret for your Snowflake OAuth application. See documentation for details on how to set up an OAuth application in Snowflake and obtain the client secret.",
27+
"validation": {
28+
"required": true
29+
},
30+
"placeholder": "Enter your Snowflake OAuth secret"
31+
},
32+
{
33+
"type": "text",
34+
"name": "oauth2Role",
35+
"label": "Role (optional)",
36+
"help": "Scope OAuth connection to a specific role. If not specified, the user's default role is used.",
37+
"validation": {
38+
"required": false
39+
},
40+
"placeholder": "Enter your Snowflake OAuth role"
41+
},
42+
{
43+
"type": "oAuth2",
44+
"name": "oauth2AuthCodeSignIn",
45+
"label": "Authorize",
46+
"validation": {
47+
"required": true
48+
}
49+
}
50+
]

0 commit comments

Comments
 (0)