From f6068292a2a6c5d712ee84dfe9cc3517add8d844 Mon Sep 17 00:00:00 2001 From: wadii Date: Mon, 27 Jul 2026 16:28:02 +0200 Subject: [PATCH 01/11] feat: clickhouse database setup SQL on the warehouse card and docs --- .../experimentation/connect-a-warehouse.md | 43 ++++++++++++++++ .../warehouse-tab/WarehouseConnectionCard.tsx | 6 +++ .../warehouse-tab/WarehouseSetupSqlHelp.tsx | 49 +++++++++++++++++++ .../tabs/warehouse-tab/clickhouseSetupSql.ts | 25 ++++++++++ 4 files changed, 123 insertions(+) create mode 100644 frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx create mode 100644 frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts diff --git a/docs/docs/experimentation/connect-a-warehouse.md b/docs/docs/experimentation/connect-a-warehouse.md index 2bd01eb3d13e..47528c0b7846 100644 --- a/docs/docs/experimentation/connect-a-warehouse.md +++ b/docs/docs/experimentation/connect-a-warehouse.md @@ -50,6 +50,49 @@ The current status is shown on the warehouse card in **Environment Settings > Wa - **Connected**: events have been received; you are ready to run experiments. - **Errored**: something went wrong; [contact support](/support/). +## Bring your own ClickHouse + +Instead of the managed Flagsmith warehouse, you can store experiment events in your own **ClickHouse** instance, so +experiment data never leaves your data platform. + +1. Create the database and events table by running the following against your ClickHouse instance. This is a one-off + setup step; the same SQL is available to copy from the warehouse card in the Flagsmith dashboard. + +```sql +CREATE DATABASE IF NOT EXISTS flagsmith_exp; + +CREATE TABLE IF NOT EXISTS flagsmith_exp.events +( + environment_key LowCardinality(String), + event LowCardinality(String), + feature_name LowCardinality(String), + timestamp DateTime64(3), + collected_at DateTime64(3), + identifier String, + value String CODEC(ZSTD(3)), + traits String CODEC(ZSTD(3)), + metadata String CODEC(ZSTD(3)), + sdk_language LowCardinality(String), + sdk_version LowCardinality(String), + + INDEX idx_identity identifier TYPE bloom_filter GRANULARITY 4, + + CONSTRAINT environment_key_not_empty CHECK environment_key != '', + CONSTRAINT event_not_empty CHECK event != '', + CONSTRAINT timestamp_sane CHECK timestamp > toDateTime64('2020-01-01 00:00:00', 3) +) +ENGINE = MergeTree +PARTITION BY toYYYYMMDD(timestamp) +ORDER BY (environment_key, event, feature_name, timestamp, identifier); +``` + +2. Go to **Environment Settings > Warehouse**, select **ClickHouse** and enter your connection details: host, port (9440 + by default, the native protocol port), database, username and password. The configured user needs `INSERT` permission + on `flagsmith_exp.events`. +3. Click **Test connection**. Once the test succeeds, save the connection. + +Connections use the ClickHouse native protocol, with TLS enabled by default. + ## Coming soon Bring your own warehouse: connections for **Snowflake**, **BigQuery** and **Databricks**, so experiment results are diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx index 4daf8b516e6f..d89cd1c9a18e 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx @@ -9,6 +9,7 @@ import Tooltip from 'components/Tooltip' import Icon from 'components/icons/Icon' import Button from 'components/base/forms/Button' import WarehouseEventCodeHelp from './WarehouseEventCodeHelp' +import WarehouseSetupSqlHelp from './WarehouseSetupSqlHelp' import WarehouseStats from './WarehouseStats' type WarehouseConnectionCardProps = { @@ -142,6 +143,11 @@ const WarehouseConnectionCard: FC = ({ )} + {connection.warehouse_type === 'clickhouse' && ( +
+ +
+ )}
{onTestConnection && ( diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx new file mode 100644 index 000000000000..22c69e1902a8 --- /dev/null +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx @@ -0,0 +1,49 @@ +import React, { FC, useState } from 'react' +import Button from 'components/base/forms/Button' +import CalloutBar from 'components/CalloutBar' +import Highlight from 'components/Highlight' +import Icon from 'components/icons/Icon' +import Utils from 'common/utils/utils' +import { CLICKHOUSE_SETUP_SQL } from './clickhouseSetupSql' + +type WarehouseSetupSqlHelpProps = { + showInitially?: boolean +} + +const WarehouseSetupSqlHelp: FC = ({ + showInitially, +}) => { + const [visible, setVisible] = useState(!!showInitially) + + return ( +
+ {'<>'}} + prefix='Database setup:' + label='Run once against your ClickHouse instance to create the events table' + expanded={visible} + onClick={() => setVisible(!visible)} + /> + {visible && ( +
+ + {CLICKHOUSE_SETUP_SQL} + +
+ +
+
+ )} +
+ ) +} + +WarehouseSetupSqlHelp.displayName = 'WarehouseSetupSqlHelp' +export default WarehouseSetupSqlHelp diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts new file mode 100644 index 000000000000..3a0d6530d4ba --- /dev/null +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts @@ -0,0 +1,25 @@ +export const CLICKHOUSE_SETUP_SQL = `CREATE DATABASE IF NOT EXISTS flagsmith_exp; + +CREATE TABLE IF NOT EXISTS flagsmith_exp.events +( + environment_key LowCardinality(String), + event LowCardinality(String), + feature_name LowCardinality(String), + timestamp DateTime64(3), + collected_at DateTime64(3), + identifier String, + value String CODEC(ZSTD(3)), + traits String CODEC(ZSTD(3)), + metadata String CODEC(ZSTD(3)), + sdk_language LowCardinality(String), + sdk_version LowCardinality(String), + + INDEX idx_identity identifier TYPE bloom_filter GRANULARITY 4, + + CONSTRAINT environment_key_not_empty CHECK environment_key != '', + CONSTRAINT event_not_empty CHECK event != '', + CONSTRAINT timestamp_sane CHECK timestamp > toDateTime64('2020-01-01 00:00:00', 3) +) +ENGINE = MergeTree +PARTITION BY toYYYYMMDD(timestamp) +ORDER BY (environment_key, event, feature_name, timestamp, identifier);` From ec16d3275c79cd0b9f65def09da6758c1ebb0c5e Mon Sep 17 00:00:00 2001 From: wadii Date: Mon, 27 Jul 2026 16:36:25 +0200 Subject: [PATCH 02/11] fix: expand setup SQL until the first event is received rather than on status --- .../tabs/warehouse-tab/WarehouseConnectionCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx index d89cd1c9a18e..2e664c6c4c22 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx @@ -145,7 +145,9 @@ const WarehouseConnectionCard: FC = ({ )} {connection.warehouse_type === 'clickhouse' && (
- +
)} From 8932ebe71300f4a2ec2987d9deb528921f0ae5f8 Mon Sep 17 00:00:00 2001 From: wadii Date: Mon, 27 Jul 2026 17:12:19 +0200 Subject: [PATCH 03/11] feat: interpolate configured database into setup SQL, default flagsmith_exp, snippet in form --- .../experimentation/connect-a-warehouse.md | 72 ++++++++++--------- .../warehouse-tab/ClickHouseConfigForm.tsx | 7 +- .../warehouse-tab/WarehouseConnectionCard.tsx | 7 ++ .../warehouse-tab/WarehouseSetupSqlHelp.tsx | 9 ++- .../__tests__/clickhouseSetupSql.test.ts | 18 +++++ .../tabs/warehouse-tab/clickhouseConfig.ts | 2 +- .../tabs/warehouse-tab/clickhouseSetupSql.ts | 6 +- 7 files changed, 79 insertions(+), 42 deletions(-) create mode 100644 frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts diff --git a/docs/docs/experimentation/connect-a-warehouse.md b/docs/docs/experimentation/connect-a-warehouse.md index 47528c0b7846..4e494cda7346 100644 --- a/docs/docs/experimentation/connect-a-warehouse.md +++ b/docs/docs/experimentation/connect-a-warehouse.md @@ -52,43 +52,45 @@ The current status is shown on the warehouse card in **Environment Settings > Wa ## Bring your own ClickHouse -Instead of the managed Flagsmith warehouse, you can store experiment events in your own **ClickHouse** instance, so -experiment data never leaves your data platform. - -1. Create the database and events table by running the following against your ClickHouse instance. This is a one-off - setup step; the same SQL is available to copy from the warehouse card in the Flagsmith dashboard. - -```sql -CREATE DATABASE IF NOT EXISTS flagsmith_exp; - -CREATE TABLE IF NOT EXISTS flagsmith_exp.events -( - environment_key LowCardinality(String), - event LowCardinality(String), - feature_name LowCardinality(String), - timestamp DateTime64(3), - collected_at DateTime64(3), - identifier String, - value String CODEC(ZSTD(3)), - traits String CODEC(ZSTD(3)), - metadata String CODEC(ZSTD(3)), - sdk_language LowCardinality(String), - sdk_version LowCardinality(String), - - INDEX idx_identity identifier TYPE bloom_filter GRANULARITY 4, - - CONSTRAINT environment_key_not_empty CHECK environment_key != '', - CONSTRAINT event_not_empty CHECK event != '', - CONSTRAINT timestamp_sane CHECK timestamp > toDateTime64('2020-01-01 00:00:00', 3) -) -ENGINE = MergeTree -PARTITION BY toYYYYMMDD(timestamp) -ORDER BY (environment_key, event, feature_name, timestamp, identifier); -``` +Instead of the managed Flagsmith warehouse, you can store experiment events in your own **ClickHouse** instance. + +1. Create the database and events table by running the following against your ClickHouse instance, using an + administrative user (the statements require `CREATE DATABASE` and `CREATE TABLE` privileges). This is a one-off setup + step; the same SQL is available to copy from the warehouse card in the Flagsmith dashboard, adjusted to the database + name you configure. + + ```sql + CREATE DATABASE IF NOT EXISTS flagsmith_exp; + + CREATE TABLE IF NOT EXISTS flagsmith_exp.events + ( + environment_key LowCardinality(String), + event LowCardinality(String), + feature_name LowCardinality(String), + timestamp DateTime64(3), + collected_at DateTime64(3), + identifier String, + value String CODEC(ZSTD(3)), + traits String CODEC(ZSTD(3)), + metadata String CODEC(ZSTD(3)), + sdk_language LowCardinality(String), + sdk_version LowCardinality(String), + + INDEX idx_identity identifier TYPE bloom_filter GRANULARITY 4, + + CONSTRAINT environment_key_not_empty CHECK environment_key != '', + CONSTRAINT event_not_empty CHECK event != '', + CONSTRAINT timestamp_sane CHECK timestamp > toDateTime64('2020-01-01 00:00:00', 3) + ) + ENGINE = MergeTree + PARTITION BY toYYYYMMDD(timestamp) + ORDER BY (environment_key, event, feature_name, timestamp, identifier); + ``` 2. Go to **Environment Settings > Warehouse**, select **ClickHouse** and enter your connection details: host, port (9440 - by default, the native protocol port), database, username and password. The configured user needs `INSERT` permission - on `flagsmith_exp.events`. + by default, the native protocol port), database (`flagsmith_exp` by default), username and password. Use a + least-privileged runtime user rather than the administrative one — it needs `INSERT` and `SELECT` on the `events` + table in the configured database. 3. Click **Test connection**. Once the test succeeds, save the connection. Connections use the ClickHouse native protocol, with TLS enabled by default. diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/ClickHouseConfigForm.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/ClickHouseConfigForm.tsx index d6f09ce9d3c7..5aa9cc71e898 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/ClickHouseConfigForm.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/ClickHouseConfigForm.tsx @@ -4,6 +4,7 @@ import Input from 'components/base/forms/Input' import Switch from 'components/Switch' import ErrorMessage from 'components/ErrorMessage' import FieldError from 'components/base/forms/FieldError' +import WarehouseSetupSqlHelp from './WarehouseSetupSqlHelp' import WarningMessage from 'components/WarningMessage' import { ClickHouseConfig } from 'common/types/responses' import { useTestWarehouseConnectionConfigMutation } from 'common/services/useWarehouseConnection' @@ -185,7 +186,7 @@ const ClickHouseConfigForm: FC = ({ onChange={(e: React.ChangeEvent) => setField(setDatabase)(e.target.value) } - placeholder='flagsmith' + placeholder='flagsmith_exp' />
@@ -230,6 +231,10 @@ const ClickHouseConfigForm: FC = ({ + + {error && }
diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx index 2e664c6c4c22..a2fbd79621ab 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx @@ -10,6 +10,7 @@ import Icon from 'components/icons/Icon' import Button from 'components/base/forms/Button' import WarehouseEventCodeHelp from './WarehouseEventCodeHelp' import WarehouseSetupSqlHelp from './WarehouseSetupSqlHelp' +import { CLICKHOUSE_DEFAULTS } from './clickhouseConfig' import WarehouseStats from './WarehouseStats' type WarehouseConnectionCardProps = { @@ -146,6 +147,12 @@ const WarehouseConnectionCard: FC = ({ {connection.warehouse_type === 'clickhouse' && (
diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx index 22c69e1902a8..79bcec14ce41 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx @@ -4,16 +4,19 @@ import CalloutBar from 'components/CalloutBar' import Highlight from 'components/Highlight' import Icon from 'components/icons/Icon' import Utils from 'common/utils/utils' -import { CLICKHOUSE_SETUP_SQL } from './clickhouseSetupSql' +import { getClickHouseSetupSql } from './clickhouseSetupSql' type WarehouseSetupSqlHelpProps = { + database: string showInitially?: boolean } const WarehouseSetupSqlHelp: FC = ({ + database, showInitially, }) => { const [visible, setVisible] = useState(!!showInitially) + const sql = getClickHouseSetupSql(database) return (
@@ -27,11 +30,11 @@ const WarehouseSetupSqlHelp: FC = ({ {visible && (
- {CLICKHOUSE_SETUP_SQL} + {sql}
)} - {isFlagsmith && !isPending && !isConnected && ( + {showSendFirstEvent && ( + {sqlExpanded && ( + <> +

+ Run this once against your ClickHouse instance using an + administrative user. Replace {''} and{' '} + {''} with your own values. +

+ + + )} +
+
+
+
+
2
+
+
+
Connect your warehouse
+

+ Enter the connection details for the user you created in step 1. +

+ +
+
+
)} {!configurableTypes.includes(selectedType) && ( diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx index 677b860de27c..4458c6fb18dc 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx @@ -1,9 +1,6 @@ import React, { FC, useState } from 'react' -import Button from 'components/base/forms/Button' import CalloutBar from 'components/CalloutBar' -import Highlight from 'components/Highlight' -import Icon from 'components/icons/Icon' -import Utils from 'common/utils/utils' +import WarehouseSqlSnippet from './WarehouseSqlSnippet' import { getClickHouseSetupSql } from './clickhouseSetupSql' type WarehouseSetupSqlHelpProps = { @@ -19,7 +16,6 @@ const WarehouseSetupSqlHelp: FC = ({ // then stays authoritative across re-renders. const [override, setOverride] = useState(null) const visible = override ?? !!showInitially - const sql = getClickHouseSetupSql(database) return (
@@ -30,23 +26,7 @@ const WarehouseSetupSqlHelp: FC = ({ expanded={visible} onClick={() => setOverride(!visible)} /> - {visible && ( -
- - {sql} - -
- -
-
- )} + {visible && }
) } diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx new file mode 100644 index 000000000000..1621e618efb1 --- /dev/null +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx @@ -0,0 +1,30 @@ +import React, { FC } from 'react' +import Button from 'components/base/forms/Button' +import Highlight from 'components/Highlight' +import Icon from 'components/icons/Icon' +import Utils from 'common/utils/utils' + +type WarehouseSqlSnippetProps = { + sql: string +} + +const WarehouseSqlSnippet: FC = ({ sql }) => ( +
+ + {sql} + +
+ +
+
+) + +WarehouseSqlSnippet.displayName = 'WarehouseSqlSnippet' +export default WarehouseSqlSnippet diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts index 33c1ce5b660b..e201013465eb 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts @@ -1,7 +1,10 @@ import fs from 'fs' import path from 'path' import { CLICKHOUSE_DEFAULTS } from 'components/pages/environment-settings/tabs/warehouse-tab/clickhouseConfig' -import { getClickHouseSetupSql } from 'components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql' +import { + getClickHouseOnboardingSql, + getClickHouseSetupSql, +} from 'components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql' describe('getClickHouseSetupSql', () => { it('interpolates the configured database into both statements', () => { @@ -17,8 +20,22 @@ describe('getClickHouseSetupSql', () => { 'CREATE TABLE IF NOT EXISTS flagsmith_exp.events', ) }) +}) + +describe('getClickHouseOnboardingSql', () => { + it('creates a placeholder user, grants it access, and includes the DDL', () => { + const sql = getClickHouseOnboardingSql() + + expect(sql).toContain( + "CREATE USER IF NOT EXISTS IDENTIFIED BY '';", + ) + expect(sql).toContain( + 'GRANT INSERT, SELECT ON flagsmith_exp.* TO ;', + ) + expect(sql).toContain(getClickHouseSetupSql('flagsmith_exp')) + }) - it('matches the copy of the DDL in the documentation', () => { + it('matches the copy of the script in the documentation', () => { const doc = fs.readFileSync( path.join( __dirname, @@ -33,6 +50,6 @@ describe('getClickHouseSetupSql', () => { .join('\n') .trim() - expect(dedented).toEqual(getClickHouseSetupSql('flagsmith_exp')) + expect(dedented).toEqual(getClickHouseOnboardingSql()) }) }) diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseConfig.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseConfig.ts index 1d4831313f0b..81bd293a927a 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseConfig.ts +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseConfig.ts @@ -5,7 +5,7 @@ export const CLICKHOUSE_DEFAULTS: ClickHouseConfig = { host: '', port: 9440, secure: true, - username: 'default', + username: '', } export type ClickHouseFormState = { diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts index 7b30835c626f..62fea650e876 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts @@ -25,3 +25,13 @@ CREATE TABLE IF NOT EXISTS ${database}.events ENGINE = MergeTree PARTITION BY toYYYYMMDD(timestamp) ORDER BY (environment_key, event, feature_name, timestamp, identifier);` + +export const getClickHouseOnboardingSql = + (): string => `-- Create a dedicated user for Flagsmith +CREATE USER IF NOT EXISTS IDENTIFIED BY ''; + +-- Allow it to write and read experiment events +GRANT INSERT, SELECT ON flagsmith_exp.* TO ; + +-- Create the database and events table +${getClickHouseSetupSql('flagsmith_exp')}` From 8df5da25b46df6aa73dde5cc075726311af972a5 Mon Sep 17 00:00:00 2001 From: wadii Date: Wed, 29 Jul 2026 09:59:57 +0200 Subject: [PATCH 07/11] feat: scoped grants and sha256 password for setup SQL user --- docs/docs/experimentation/connect-a-warehouse.md | 13 ++++++++----- .../__tests__/clickhouseSetupSql.test.ts | 8 ++++++-- .../tabs/warehouse-tab/clickhouseSetupSql.ts | 8 ++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/docs/experimentation/connect-a-warehouse.md b/docs/docs/experimentation/connect-a-warehouse.md index ae98c25f6284..1b28b31293bc 100644 --- a/docs/docs/experimentation/connect-a-warehouse.md +++ b/docs/docs/experimentation/connect-a-warehouse.md @@ -55,16 +55,19 @@ The current status is shown on the warehouse card in **Environment Settings > Wa Instead of the managed Flagsmith warehouse, you can store experiment events in your own **ClickHouse** instance. 1. Configure your instance by running the following against it, using an administrative user (the statements require - `CREATE USER`, `GRANT`, `CREATE DATABASE` and `CREATE TABLE` privileges). Replace `` and - `` with your own values. This is a one-off setup step; the same SQL is available to copy from - the Flagsmith dashboard. + `CREATE USER`, `GRANT`, `CREATE DATABASE` and `CREATE TABLE` privileges). Replace `` and `` + with your own values. This is a one-off setup step; the same SQL is available to copy from the Flagsmith dashboard. ```sql -- Create a dedicated user for Flagsmith - CREATE USER IF NOT EXISTS IDENTIFIED BY ''; + CREATE USER IF NOT EXISTS + IDENTIFIED WITH sha256_password BY ''; -- Allow it to write and read experiment events - GRANT INSERT, SELECT ON flagsmith_exp.* TO ; + GRANT SELECT, INSERT ON flagsmith_exp.events TO ; + + -- Allow it to check the events table exists + GRANT SHOW TABLES, SHOW COLUMNS ON flagsmith_exp.* TO ; -- Create the database and events table CREATE DATABASE IF NOT EXISTS flagsmith_exp; diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts index e201013465eb..a5562eec5a1b 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/__tests__/clickhouseSetupSql.test.ts @@ -26,11 +26,15 @@ describe('getClickHouseOnboardingSql', () => { it('creates a placeholder user, grants it access, and includes the DDL', () => { const sql = getClickHouseOnboardingSql() + expect(sql).toContain('CREATE USER IF NOT EXISTS ') expect(sql).toContain( - "CREATE USER IF NOT EXISTS IDENTIFIED BY '';", + "IDENTIFIED WITH sha256_password BY '';", ) expect(sql).toContain( - 'GRANT INSERT, SELECT ON flagsmith_exp.* TO ;', + 'GRANT SELECT, INSERT ON flagsmith_exp.events TO ;', + ) + expect(sql).toContain( + 'GRANT SHOW TABLES, SHOW COLUMNS ON flagsmith_exp.* TO ;', ) expect(sql).toContain(getClickHouseSetupSql('flagsmith_exp')) }) diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts index 62fea650e876..048829449399 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/clickhouseSetupSql.ts @@ -28,10 +28,14 @@ ORDER BY (environment_key, event, feature_name, timestamp, identifier);` export const getClickHouseOnboardingSql = (): string => `-- Create a dedicated user for Flagsmith -CREATE USER IF NOT EXISTS IDENTIFIED BY ''; +CREATE USER IF NOT EXISTS + IDENTIFIED WITH sha256_password BY ''; -- Allow it to write and read experiment events -GRANT INSERT, SELECT ON flagsmith_exp.* TO ; +GRANT SELECT, INSERT ON flagsmith_exp.events TO ; + +-- Allow it to check the events table exists +GRANT SHOW TABLES, SHOW COLUMNS ON flagsmith_exp.* TO ; -- Create the database and events table ${getClickHouseSetupSql('flagsmith_exp')}` From fbfde0a08c0cf102eb18e8edb95514edc3b114b2 Mon Sep 17 00:00:00 2001 From: wadii Date: Wed, 29 Jul 2026 11:27:56 +0200 Subject: [PATCH 08/11] feat: collapse database setup below the SDK snippet and hide test connection once connected --- .../tabs/warehouse-tab/WarehouseConnectionCard.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx index 90ae56f851a7..4989590c7ffe 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseConnectionCard.tsx @@ -63,7 +63,8 @@ const WarehouseConnectionCard: FC = ({ // until events actually arrive in the customer's warehouse. const showSendFirstEvent = isFlagsmith ? !isPending && !isConnected - : !connection.total_events_received + : connection.warehouse_type === 'clickhouse' && + !connection.total_events_received const handleDelete = () => { openConfirm({ @@ -149,8 +150,9 @@ const WarehouseConnectionCard: FC = ({
)} + {connection.warehouse_type === 'clickhouse' && ( -
+
= ({ connection.config.database) || CLICKHOUSE_DEFAULTS.database } - showInitially={!connection.total_events_received} />
)} -
- {onTestConnection && ( + {onTestConnection && !isConnected && ( + {sqlExpanded && ( <>

diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx index 4458c6fb18dc..4c499c14e425 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSetupSqlHelp.tsx @@ -20,7 +20,6 @@ const WarehouseSetupSqlHelp: FC = ({ return (

{'<>'}} prefix='Database setup:' label='Run once against your ClickHouse instance to create the events table' expanded={visible} diff --git a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx index 1621e618efb1..d2ffb9e0b4a2 100644 --- a/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx +++ b/frontend/web/components/pages/environment-settings/tabs/warehouse-tab/WarehouseSqlSnippet.tsx @@ -19,7 +19,7 @@ const WarehouseSqlSnippet: FC = ({ sql }) => ( theme='primary' size='xSmall' > - + Copy Code