Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions plugins/Snowflake/v1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Snowflake plugin

A simple data source for Snowflake that supports Snowflake SQL queries. Requires an OAuth connection.
43 changes: 43 additions & 0 deletions plugins/Snowflake/v1/configValidation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"steps": [
{
"displayName": "API access",
"dataStream": {
"name": "sqlQuery",
"config": {
"query": "show databases"
}
},
"success": "User credentials has Snowflake query permissions.",
"error": "User does not have permission to access the Snowflake API (query 'SHOW DATABASES' failed).",
"required": true
},
{
"displayName": "Compute access",
"dataStream": {
"name": "sqlQuery",
"config": {
"query": "select 1/1",
"errorOnEmptyResults": true
}
},
"success": "User has access to warehouse.",
"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.",
"required": true
},
{
"displayName": "Database access",
"dataStream": {
"name": "sqlQuery",
"config": {
"query": "show databases",
"errorOnEmptyResults": true
}
},
"success": "User has access to at least one database.",
"error": "User does not have permission to access any databases. Check user's default role or specify a role.",
"required": false
}
]
}

36 changes: 36 additions & 0 deletions plugins/Snowflake/v1/dataStreams/scripts/sqlQuery-post.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
result = data.data.map( r => r.reduce((obj, value, i) => {
const columnName = data.resultSetMetaData.rowType[i].name;
obj[columnName] = value;
return obj;
}, {}));

// support value column for autoComplete queries
if (context.config.valueColumn) {
metadata = [
{
name: context.config.valueColumn,
role: "value"
}
]
} else {
const typeMapping = {
"text": "string",
"fixed": "number",
"real": "number",
"varchar": "string",
"date": "date",
"timestamp": "date"
};

metadata = data.resultSetMetaData.rowType.map( c => {
return {
name: c.name,
shape: typeMapping[c.type] || "string"
}
});
}

// used for validation queries
if (context.config.errorOnEmptyResults === true && data.data.length === 0) {
throw new Error("No results");
}
77 changes: 77 additions & 0 deletions plugins/Snowflake/v1/dataStreams/sqlQuery.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"name": "sqlQuery",
"displayName": "SQL Query",
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "post",
"paging": {
"mode": "none"
},
"expandInnerObjects": true,
"endpointPath": "/v2/statements/",
"postBody": {
"database": "{{typeof database !== 'undefined' ? database : undefined}}",
"schema": "{{typeof schema !== 'undefined' ? schema : undefined}}",
"statement": "{{query}}"
},
"postRequestScript": "sqlQuery-post.js",
"getArgs": [],
"headers": []
},
"ui": [
{
"name": "database",
"type": "autocomplete",
"label": "Database",
"validation": {
"required": false
},
"isMulti": false,
"allowCustomValues": true,
"data": {
"source": "dataStream",
"dataStreamName": "sqlQuery",
"dataSourceConfig": {
"valueColumn": "name",
"query": "show databases"
}
}
},
{
"name": "schema",
"type": "autocomplete",
"label": "Schema",
"validation": {
"required": false
},
"isMulti": false,
"allowCustomValues": true,
"data": {
"source": "dataStream",
"dataStreamName": "sqlQuery",
"dataSourceConfig": {
"valueColumn": "name",
"database": {
"fieldName": "database",
"required": true
},
"query": "show schemas"
}
}
},
{
"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}}'",
"name": "query",
"language": "sql",
"label": "SQL query",
"type": "code",
"validation": {
"required": true
}
}
],
"manualConfigApply": true,
"supportsNoneTimeframe": true,
"requiresParameterTimeframe": true,
"defaultTimeframe": "none"
}
99 changes: 99 additions & 0 deletions plugins/Snowflake/v1/docs/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Before you start

## Creating an OAuth integration in Snowflake

The Snowflake data source authenticates using OAuth.

Comment thread
richbenwell marked this conversation as resolved.
Before configuring the data source you will need to register SquaredUp with your Snowflake account by creating a custom integration.

Sample Snowflake commands for creating the integration are provided below.

For more information on creating a Snowflake integration see:
https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-snowflake


If your SquaredUp account is in the US region (default):

```
CREATE SECURITY INTEGRATION oauth_squaredup
TYPE = oauth
OAUTH_CLIENT = custom
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
OAUTH_REDIRECT_URI = 'https://app.squaredup.com/settings/pluginsoauth2'
COMMENT = 'Used by SquaredUp to connect to this Snowflake account'
```

If your SquaredUp account is in the EU region:

```
CREATE SECURITY INTEGRATION oauth_squaredup
TYPE = oauth
Comment thread
richbenwell marked this conversation as resolved.
OAUTH_CLIENT = custom
OAUTH_CLIENT_TYPE = 'CONFIDENTIAL'
OAUTH_REDIRECT_URI = 'https://eu.app.squaredup.com/settings/pluginsoauth2'
COMMENT = 'Used by SquaredUp to connect to this Snowflake account'
```

Once your integration is created, run:

```
SELECT
oauth:OAUTH_CLIENT_SECRET::STRING AS OAUTH_CLIENT_SECRET,
oauth:OAUTH_CLIENT_ID::STRING AS OAUTH_CLIENT_ID
FROM (SELECT PARSE_JSON(SYSTEM$SHOW_OAUTH_CLIENT_SECRETS('oauth_squaredup')) AS oauth)

```

Use the values of the `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` columns in your configuration below.


## Creating a read-only user

To connect to Snowflake you will need the credentials for a Snowflake user.

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

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.

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.


# Configuration

## Snowflake account identifier

Enter your Snowflake account identifier.

This can be found in the Snowflake portal under 'Your Username' > Account > Account Identifier.
Comment thread
richbenwell marked this conversation as resolved.

The account identifier is in the format <org_name>-<account_name>.

For example: `ABCDEFG-XYZ12345`

Alternatively, run the following Snowflake query:

```
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();
```

## Snowflake OAuth client ID

The client ID for your Snowflake OAuth application.

Enter the `OAUTH_CLIENT_ID` value from the integration you created above.

## Snowflake OAuth client secret

The client secret for your Snowflake OAuth application.

Enter the `OAUTH_CLIENT_SECRET` value from the integration you created above.

## Role (optional)

Restrict OAuth connection to a specific role. If not specified, the user's default role is used.

If you have created a custom role for your database, for example a read-only role, enter its name here.

## Authorize

Click the Sign-in button to authorize SquaredUp to access Snowflake.
1 change: 1 addition & 0 deletions plugins/Snowflake/v1/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions plugins/Snowflake/v1/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "snowflake",
"displayName": "Snowflake",
"version": "1.0.0",
"author": {
"name": "SquaredUp Labs",
"type": "labs"
},
"description": "Query data from Snowflake.",
"category": "Database",
"type": "hybrid",
"schemaVersion": "2.0",
"base": {
"plugin": "WebAPI",
"majorVersion": "1",
"config": {
"queryArgs": [],
"headers": [],
"oauth2TokenExtraArgs": [],
"oauth2ClientSecret": "{{oauth2ClientSecret}}",
"oauth2ClientSecretLocationDuringAuth": "header",
"oauth2AuthUrl": "https://{{accountId}}.snowflakecomputing.com/oauth/authorize",
"authMode": "oauth2",
"oauth2GrantType": "authCode",
"baseUrl": "https://{{accountId}}.snowflakecomputing.com/api",
"oauth2TokenExtraHeaders": [
{
"value": "application/x-www-form-urlencoded",
"key": "Content-Type"
}
],
"oauth2ClientId": "{{oauth2ClientId}}",
"oauth2TokenUrl": "https://{{accountId}}.snowflakecomputing.com/oauth/token-request",
"oauth2AuthExtraArgs": [],
"oauth2Scope": "refresh_token {{oauth2Role? 'session:role:' + oauth2Role : ''}}"
}
},
"links": [
{
"category": "documentation",
"url": "https://github.com/squaredup/plugins/blob/main/plugins/Snowflake/v1/docs/setup.md",
"label": "Help adding this plugin"
},
{
"category": "source",
"url": "https://github.com/squaredup/plugins/tree/main/plugins/Snowflake/v1",
"label": "Repository"
}
]
}
50 changes: 50 additions & 0 deletions plugins/Snowflake/v1/ui.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"type": "text",
"name": "accountId",
"label": "Snowflake account identifier",
"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",
"validation": {
"required": true
},
"placeholder": "<org_name>-<account_name>, e.g. ABCDEFG-XYZ12345"
},
{
"type": "text",
"name": "oauth2ClientId",
"label": "Snowflake OAuth client ID",
"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.",
"validation": {
"required": true
},
"placeholder": "Enter your Snowflake OAuth client ID"
},
{
"type": "password",
"name": "oauth2ClientSecret",
"label": "Snowflake OAuth secret",
"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.",
"validation": {
"required": true
},
"placeholder": "Enter your Snowflake OAuth secret"
},
{
"type": "text",
"name": "oauth2Role",
"label": "Role (optional)",
"help": "Scope OAuth connection to a specific role. If not specified, the user's default role is used.",
"validation": {
"required": false
},
"placeholder": "Enter your Snowflake OAuth role"
},
{
"type": "oAuth2",
"name": "oauth2AuthCodeSignIn",
"label": "Authorize",
"validation": {
"required": true
}
}
]
Loading