Compare commits
3 Commits
python-run
...
nmezzopera
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e5279cddb | ||
|
|
a838898361 | ||
|
|
40347e5408 |
7
.github/workflows/app-db.yml
vendored
7
.github/workflows/app-db.yml
vendored
@@ -2,10 +2,6 @@ name: App DB Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-app-db
|
||||
@@ -13,7 +9,6 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
be-tests-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -125,7 +120,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -237,7 +231,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
|
||||
216
.github/workflows/drivers-legacy.yml
vendored
Normal file
216
.github/workflows/drivers-legacy.yml
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
name: Driver Tests - Legacy Versions
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run daily at 2 AM UTC
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-legacy
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Legacy versions: older database versions tested on schedule
|
||||
be-tests-postgres-12:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: PostgreSQL 12.x
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test PostgreSQL 12.x
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-postgres-12-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/old-migrations-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql-8:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
name: MySQL 8.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MySQL 8.0
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mysql-8-0-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/old-migrations-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mariadb-10-2:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
services:
|
||||
mariadb:
|
||||
image: circleci/mariadb:10.2.23
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
name: MariaDB 10.2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MariaDB 10.2
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mariadb-10-2-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/old-migrations-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-oracle-18-4:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: oracle
|
||||
MB_ORACLE_TEST_HOST: localhost
|
||||
MB_ORACLE_TEST_USER: system
|
||||
MB_ORACLE_TEST_PASSWORD: password
|
||||
MB_ORACLE_TEST_SERVICE_NAME: XEPDB1
|
||||
services:
|
||||
oracle:
|
||||
image: gvenzl/oracle-xe:18.4.0-slim
|
||||
env:
|
||||
ORACLE_PASSWORD: password
|
||||
ports:
|
||||
- "1521:1521"
|
||||
name: Oracle 18.4
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Oracle 18.4
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-oracle-18-4-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/old-migrations-test]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
legacy-results:
|
||||
needs:
|
||||
- be-tests-postgres-12
|
||||
- be-tests-mysql-8
|
||||
- be-tests-mariadb-10-2
|
||||
- be-tests-oracle-18-4
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: legacy-results
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check legacy driver status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | #js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
const jobs = Object.entries(needs).map(([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("✅ Legacy drivers passed!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("❌ Legacy drivers failed:");
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success' && job.result !== 'skipped') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
191
.github/workflows/drivers-tier1.yml
vendored
Normal file
191
.github/workflows/drivers-tier1.yml
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
name: Driver Tests - Tier 1 (Fast)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-tier1
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Tier 1: Fast drivers (5-15 minutes, no complex setup)
|
||||
be-tests-h2:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
CI: 'true'
|
||||
name: H2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test H2 driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-h2'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-sqlite:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: sqlite
|
||||
name: SQLite
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test SQLite driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-sqlite-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres-latest:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
MB_POSTGRES_SSL_TEST_SSL: 'true'
|
||||
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
|
||||
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: PostgreSQL Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test PostgreSQL Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-postgres-latest-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql-latest:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
MB_MYSQL_SSL_TEST_SSL: 'true'
|
||||
MB_MYSQL_SSL_TEST_HOST: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_HOST }}
|
||||
MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS: 'verifyServerCertificate=true'
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
name: MySQL Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MySQL Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mysql-latest-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
tier1-results:
|
||||
needs:
|
||||
- be-tests-h2
|
||||
- be-tests-sqlite
|
||||
- be-tests-postgres-latest
|
||||
- be-tests-mysql-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: tier1-results
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check tier 1 status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | #js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
const jobs = Object.entries(needs).map(([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("✅ Tier 1 (Fast) drivers passed!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("❌ Tier 1 (Fast) drivers failed:");
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
200
.github/workflows/drivers-tier2.yml
vendored
Normal file
200
.github/workflows/drivers-tier2.yml
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
name: Driver Tests - Tier 2 (Medium)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-tier2
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Tier 2: Medium drivers (15-35 minutes, cloud services/docker setup)
|
||||
be-tests-bigquery-cloud-sdk:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: bigquery-cloud-sdk
|
||||
MB_BIGQUERY_TEST_PROJECT_ID: ${{ secrets.BIGQUERY_TEST_PROJECT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_ID: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_ID }}
|
||||
MB_BIGQUERY_TEST_CLIENT_SECRET: ${{ secrets.MB_BIGQUERY_TEST_CLIENT_SECRET }}
|
||||
MB_BIGQUERY_TEST_ACCESS_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_ACCESS_TOKEN }}
|
||||
MB_BIGQUERY_TEST_REFRESH_TOKEN: ${{ secrets.MB_BIGQUERY_TEST_REFRESH_TOKEN }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON }}
|
||||
MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING: ${{ secrets.MB_BIGQUERY_CLOUD_SDK_TEST_SERVICE_ACCOUNT_JSON_ROUTING }}
|
||||
name: BigQuery
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test BigQuery Cloud SDK driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-bigquery-cloud-sdk-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-snowflake:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: snowflake
|
||||
MB_SNOWFLAKE_TEST_USER: METABASE CI
|
||||
MB_SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.MB_SNOWFLAKE_TEST_ACCOUNT }}
|
||||
MB_SNOWFLAKE_TEST_PASSWORD: ${{ secrets.MB_SNOWFLAKE_TEST_PASSWORD }}
|
||||
MB_SNOWFLAKE_TEST_WAREHOUSE: ${{ secrets.MB_SNOWFLAKE_TEST_WAREHOUSE }}
|
||||
MB_SNOWFLAKE_TEST_PK_USER: METABASE PK
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PRIVATE_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY }}
|
||||
MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2: ${{ secrets.MB_SNOWFLAKE_TEST_PK_PUBLIC_KEY_2 }}
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DEFAULT_USER: RSA_ROLE_TEST_DEFAULT_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_CUSTOM_USER: RSA_ROLE_TEST_CUSTOM_USER
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_ROLE: RSA_ROLE_TEST_ROLE
|
||||
MB_SNOWFLAKE_TEST_RSA_ROLE_TEST_DB: RSA_ROLE_TEST_DB
|
||||
name: Snowflake
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Snowflake driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-snowflake-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-redshift-standard:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: redshift
|
||||
MB_REDSHIFT_TEST_USER: metabase_ci
|
||||
MB_REDSHIFT_TEST_DB: testdb
|
||||
MB_REDSHIFT_TEST_DB_ROUTING: dev
|
||||
MB_REDSHIFT_TEST_HOST: ${{ secrets.MB_REDSHIFT_TEST_HOST }}
|
||||
MB_REDSHIFT_TEST_PASSWORD: ${{ secrets.MB_REDSHIFT_TEST_PASSWORD }}
|
||||
name: Redshift (Standard)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Redshift driver (excluding upload tests)
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-redshift-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
:exclude-tags [:mb/upload-tests]
|
||||
:fail-fast? true
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-clickhouse:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 35
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: clickhouse
|
||||
MB_CLICKHOUSE_TEST_HOST: localhost
|
||||
MB_CLICKHOUSE_TEST_PORT: 8123
|
||||
MB_CLICKHOUSE_TEST_OLD_PORT: 8124
|
||||
MB_CLICKHOUSE_TEST_NGINX_PORT: 8127
|
||||
name: ClickHouse
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add ClickHouse TLS instance to /etc/hosts
|
||||
run: |
|
||||
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
|
||||
- name: Start ClickHouse in Docker
|
||||
uses: hoverkraft-tech/compose-action@v2.0.0
|
||||
with:
|
||||
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
|
||||
down-flags: "--volumes"
|
||||
services: |
|
||||
clickhouse
|
||||
clickhouse_tls
|
||||
clickhouse_older_version
|
||||
clickhouse_cluster_node1
|
||||
clickhouse_cluster_node2
|
||||
nginx
|
||||
- name: Test ClickHouse driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-clickhouse-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
tier2-results:
|
||||
needs:
|
||||
- be-tests-bigquery-cloud-sdk
|
||||
- be-tests-snowflake
|
||||
- be-tests-redshift-standard
|
||||
- be-tests-clickhouse
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: tier2-results
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check tier 2 status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | #js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
const jobs = Object.entries(needs).map(([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("✅ Tier 2 (Medium) drivers passed!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("❌ Tier 2 (Medium) drivers failed:");
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
256
.github/workflows/drivers-tier3.yml
vendored
Normal file
256
.github/workflows/drivers-tier3.yml
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
name: Driver Tests - Tier 3 (Slow/Complex)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-tier3
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Determine if tier 3 drivers should run (same logic as current determine-driver-skips)
|
||||
determine-tier3-skips:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
skip: ${{ steps.check_team_and_branch.outputs.skip }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch base branch
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.base.ref != ''
|
||||
run: |
|
||||
git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
|
||||
- name: Check if tier3 drivers should run
|
||||
id: check_team_and_branch
|
||||
run: |
|
||||
IS_MASTER_OR_RELEASE="${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-') }}"
|
||||
HAS_TEAM_LABEL="${{ contains(github.event.pull_request.labels.*.name, '.Team/Drivers') || contains(github.event.pull_request.labels.*.name, '.Team/Querying') || contains(github.event.pull_request.labels.*.name, '.Team/SemanticLayer') || contains(github.event.pull_request.labels.*.name, '.Team/DevEx') }}"
|
||||
|
||||
echo "Is master or release: ${IS_MASTER_OR_RELEASE}"
|
||||
echo "Has relevant team label: ${HAS_TEAM_LABEL}"
|
||||
if [[ "${IS_MASTER_OR_RELEASE}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Merging into master or a release branch. Tier 3 driver tests will run."
|
||||
elif [[ "${HAS_TEAM_LABEL}" == "true" ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "A relevant team label is present. Tier 3 driver tests will run."
|
||||
else
|
||||
echo "Checking for changes in relevant folders..."
|
||||
CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }})
|
||||
|
||||
CHANGED_FOLDERS=()
|
||||
for folder in "src/metabase/lib" "src/metabase/query_processor" "modules/drivers" "src/metabase/driver"; do
|
||||
if echo "$CHANGED_FILES" | grep -q "$folder"; then
|
||||
CHANGED_FOLDERS+=("$folder")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#CHANGED_FOLDERS[@]} -gt 0 ]]; then
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected in: ${CHANGED_FOLDERS[*]}. Tier 3 driver tests will run."
|
||||
else
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
echo "No relevant changes found. Tier 3 driver tests will be skipped."
|
||||
fi
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
# Tier 3: Complex drivers (30-60 minutes, complex setup, external dependencies)
|
||||
be-tests-athena:
|
||||
needs: determine-tier3-skips
|
||||
if: ${{ !(needs.determine-tier3-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 65
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: athena
|
||||
MB_ATHENA_TEST_REGION: us-east-1
|
||||
MB_ATHENA_TEST_REGION_ROUTING: us-east-2
|
||||
MB_ATHENA_TEST_ACCESS_KEY: ${{ secrets.MB_ATHENA_TEST_ACCESS_KEY }}
|
||||
MB_ATHENA_TEST_SECRET_KEY: ${{ secrets.MB_ATHENA_TEST_SECRET_KEY }}
|
||||
MB_ATHENA_TEST_S3_STAGING_DIR: ${{ secrets.MB_ATHENA_TEST_S3_STAGING_DIR }}
|
||||
MB_ATHENA_TEST_S3_STAGING_DIR_ROUTING: ${{ secrets.MB_ATHENA_TEST_S3_STAGING_DIR_ROUTING }}
|
||||
MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_ACCESS_KEY: ${{ secrets.MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_ACCESS_KEY }}
|
||||
MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_SECRET_KEY: ${{ secrets.MB_ATHENA_TEST_WITHOUT_GET_TABLE_METADATA_SECRET_KEY }}
|
||||
name: Athena
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Athena driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-athena-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-databricks:
|
||||
needs: determine-tier3-skips
|
||||
if: ${{ !(needs.determine-tier3-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 65
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: databricks
|
||||
MB_DATABRICKS_TEST_HOST: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HOST }}
|
||||
MB_DATABRICKS_TEST_HTTP_PATH: ${{ secrets.MB_DATABRICKS_JDBC_TEST_HTTP_PATH }}
|
||||
MB_DATABRICKS_TEST_TOKEN: ${{ secrets.MB_DATABRICKS_JDBC_TEST_TOKEN }}
|
||||
MB_DATABRICKS_TEST_CATALOG: 'metabase_ci'
|
||||
MB_DATABRICKS_TEST_CATALOG_ROUTING: 'metabase_routing_catalog'
|
||||
MB_DATABRICKS_TEST_CLIENT_ID: ${{ secrets.MB_DATABRICKS_JDBC_TEST_CLIENT_ID }}
|
||||
MB_DATABRICKS_TEST_OAUTH_SECRET: ${{ secrets.MB_DATABRICKS_JDBC_TEST_OAUTH_SECRET }}
|
||||
name: Databricks
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Databricks driver
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-databricks-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-oracle-latest:
|
||||
needs: determine-tier3-skips
|
||||
if: ${{ !(needs.determine-tier3-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 65
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: oracle
|
||||
MB_ORACLE_TEST_HOST: localhost
|
||||
MB_ORACLE_TEST_USER: ''
|
||||
MB_ORACLE_TEST_SERVICE_NAME: XEPDB1
|
||||
MB_ORACLE_TEST_PASSWORD: ''
|
||||
MB_ORACLE_TEST_SSL: true
|
||||
MB_ORACLE_SSL_TEST_SSL: true
|
||||
MB_ORACLE_TEST_SSL_USE_TRUSTSTORE: true
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_PATH: './test_resources/ssl/oracle/truststore.p12'
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_OPTIONS: local
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_PASSWORD_VALUE: 'PassworD_#1234'
|
||||
MB_ORACLE_TEST_SSL_USE_KEYSTORE: true
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_PATH: './test_resources/ssl/oracle/keystore.p12'
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_OPTIONS: local
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_PASSWORD_VALUE: 'PassworD_#1234'
|
||||
services:
|
||||
oracle:
|
||||
image: metabase/qa-databases:oracle-xe-21.3
|
||||
env:
|
||||
ORACLE_PASSWORD: password
|
||||
ports:
|
||||
- "1521:2484"
|
||||
name: Oracle 21.3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test Oracle 21.3
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-oracle-21-3-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mongo-latest:
|
||||
needs: determine-tier3-skips
|
||||
if: ${{ !(needs.determine-tier3-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 65
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mongo
|
||||
MB_MONGO_TEST_USER: metabase
|
||||
MB_MONGO_TEST_PASSWORD: metasample123
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:latest
|
||||
ports:
|
||||
- "27017:27017"
|
||||
env:
|
||||
MONGO_INITDB_ROOT_USERNAME: metabase
|
||||
MONGO_INITDB_ROOT_PASSWORD: metasample123
|
||||
name: MongoDB Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MongoDB Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mongo-latest-ee'
|
||||
test-args: >-
|
||||
:exclude-tags [:mongo-sharded-cluster-tests]
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
tier3-results:
|
||||
needs:
|
||||
- be-tests-athena
|
||||
- be-tests-databricks
|
||||
- be-tests-oracle-latest
|
||||
- be-tests-mongo-latest
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
name: tier3-results
|
||||
if: always() && !cancelled()
|
||||
steps:
|
||||
- name: Check tier 3 status
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
needs: ${{ toJson(needs) }}
|
||||
with:
|
||||
script: | #js
|
||||
const needs = JSON.parse(process.env.needs);
|
||||
const jobs = Object.entries(needs).map(([jobName, jobValues]) => ({
|
||||
name: jobName,
|
||||
result: jobValues.result
|
||||
}));
|
||||
|
||||
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
||||
console.log("✅ Tier 3 (Slow/Complex) drivers passed!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("❌ Tier 3 (Slow/Complex) drivers failed:");
|
||||
jobs.forEach((job) => {
|
||||
if (job.result !== 'success') {
|
||||
console.log(`${job.name} - ${job.result}`);
|
||||
}
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
258
.github/workflows/drivers.yml
vendored
258
.github/workflows/drivers.yml
vendored
@@ -2,10 +2,6 @@ name: Driver Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
skip:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-drivers
|
||||
@@ -66,7 +62,7 @@ jobs:
|
||||
shell: bash
|
||||
be-tests-athena-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -103,7 +99,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-bigquery-cloud-sdk-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -138,7 +133,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-clickhouse-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -186,7 +180,7 @@ jobs:
|
||||
|
||||
be-tests-druid-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -222,7 +216,7 @@ jobs:
|
||||
|
||||
be-tests-druid-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -258,7 +252,7 @@ jobs:
|
||||
|
||||
be-tests-databricks-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -294,7 +288,7 @@ jobs:
|
||||
|
||||
be-tests-databricks-multi-catalog-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -330,7 +324,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-h2:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -356,64 +349,18 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mysql-mariadb:
|
||||
if: ${{ !inputs.skip }}
|
||||
be-tests-mysql-latest:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: MariaDB 10.2
|
||||
junit-name: be-tests-mariadb-10-2-ee
|
||||
image: circleci/mariadb:10.2.23
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MariaDB Latest
|
||||
junit-name: be-tests-mariadb-latest-ee
|
||||
image: circleci/mariadb:latest
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MySQL 8.0
|
||||
junit-name: be-tests-mysql-8-0-ee
|
||||
image: mysql:8.0
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: MySQL Latest
|
||||
junit-name: be-tests-mysql-latest-ee
|
||||
image: mysql:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: ${{ matrix.version.image }}
|
||||
image: mysql:latest
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
|
||||
# it in to the command below.
|
||||
__ADDITIONAL_EXCLUDED_TAG__: >-
|
||||
${{
|
||||
(
|
||||
github.event_name == 'push' &&
|
||||
(
|
||||
github.ref == 'master' ||
|
||||
startsWith(github.ref, 'release-')
|
||||
) &&
|
||||
''
|
||||
) ||
|
||||
':mb/old-migrations-test'
|
||||
}}
|
||||
# actual serious env vars below
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
@@ -422,33 +369,63 @@ jobs:
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
#
|
||||
# SSL config: only for mysql latest tests.
|
||||
#
|
||||
# set up env vars for something named "MYSQL_SSL" to run MySQL SSL tests verifying connectivity with PEM cert
|
||||
# they are deliberately given a different name to prevent them from affecting the regular test run against
|
||||
# the configured MySQL instance, but there is one particular test (mysql-connect-with-ssl-and-pem-cert-test)
|
||||
# that overrides the MB_MYSQL_TEST_* values with them
|
||||
# the MYSQL_RDS_SSL_INSTANCE vars are defined as secrets and can be altered
|
||||
MB_MYSQL_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
# SSL configuration for MySQL latest
|
||||
MB_MYSQL_SSL_TEST_SSL: 'true'
|
||||
MB_MYSQL_SSL_TEST_HOST: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_HOST }}
|
||||
MB_MYSQL_SSL_TEST_ADDITIONAL_OPTIONS: 'verifyServerCertificate=true'
|
||||
# the contents of the ./resources/certificates/rds-combined-ca-bundle.pem file
|
||||
MB_MYSQL_SSL_TEST_SSL_CERT: ${{ secrets.MB_MYSQL_SSL_TEST_SSL_CERT }}
|
||||
MB_MYSQL_SSL_TEST_USER: metabase
|
||||
MB_MYSQL_SSL_TEST_PASSWORD: ${{ secrets.MYSQL_RDS_SSL_INSTANCE_PASSWORD }}
|
||||
# for historic reasons (I don't want to mess around with required jobs) the job name should be something like
|
||||
# "be-tests-mariadb-10-2-ee (0)"
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
name: MySQL Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
- name: Test MySQL Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
junit-name: 'be-tests-mysql-latest-ee'
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
with:
|
||||
input-path: ./target/junit/
|
||||
output-name: ${{ github.job }}
|
||||
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
|
||||
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-mariadb-latest:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
services:
|
||||
mariadb:
|
||||
image: circleci/mariadb:latest
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: circle_test
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: mysql
|
||||
MB_DB_TYPE: mysql
|
||||
MB_DB_HOST: localhost
|
||||
MB_DB_PORT: 3306
|
||||
MB_DB_DBNAME: circle_test
|
||||
MB_DB_USER: root
|
||||
MB_MYSQL_TEST_USER: root
|
||||
name: MariaDB Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test MariaDB Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-mariadb-latest-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -463,7 +440,7 @@ jobs:
|
||||
|
||||
be-tests-mongo:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -513,7 +490,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-ssl:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -578,7 +555,7 @@ jobs:
|
||||
|
||||
be-tests-mongo-sharded-cluster-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -610,64 +587,41 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-oracle:
|
||||
be-tests-oracle-21:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Oracle 18.4
|
||||
junit-name: be-tests-oracle-18-4-ee
|
||||
image: gvenzl/oracle-xe:18.4.0-slim
|
||||
port: 1521
|
||||
env:
|
||||
user: system
|
||||
password: 'password'
|
||||
enable-ssl-tests: false
|
||||
- name: Oracle 21.3
|
||||
junit-name: be-tests-oracle-21-3-ee
|
||||
image: metabase/qa-databases:oracle-xe-21.3
|
||||
port: 2484
|
||||
env:
|
||||
user: ''
|
||||
password: ''
|
||||
enable-ssl-tests: true
|
||||
env:
|
||||
CI: 'true'
|
||||
DRIVERS: oracle
|
||||
MB_ORACLE_TEST_HOST: localhost
|
||||
MB_ORACLE_TEST_USER: '${{ matrix.version.env.user }}'
|
||||
MB_ORACLE_TEST_PORT: 2484
|
||||
MB_ORACLE_TEST_SERVICE_NAME: XEPDB1
|
||||
# Only the non-SSL 18.4 tests specify password as an env var; the SSL 21.3 tests get it from the keystore I guess
|
||||
MB_ORACLE_TEST_PASSWORD: '${{ matrix.version.env.password }}'
|
||||
# These are ignored for the 18.4 tests which do not test SSL
|
||||
MB_ORACLE_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_ORACLE_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_ORACLE_TEST_SSL_USE_TRUSTSTORE: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_ORACLE_TEST_SSL: 'true'
|
||||
MB_ORACLE_SSL_TEST_SSL: 'true'
|
||||
MB_ORACLE_TEST_SSL_USE_TRUSTSTORE: 'true'
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_PATH: './test_resources/ssl/oracle/truststore.p12'
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_OPTIONS: local
|
||||
MB_ORACLE_TEST_SSL_TRUSTSTORE_PASSWORD_VALUE: 'PassworD_#1234'
|
||||
MB_ORACLE_TEST_SSL_USE_KEYSTORE: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
MB_ORACLE_TEST_SSL_USE_KEYSTORE: 'true'
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_PATH: './test_resources/ssl/oracle/keystore.p12'
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_OPTIONS: local
|
||||
MB_ORACLE_TEST_SSL_KEYSTORE_PASSWORD_VALUE: 'PassworD_#1234'
|
||||
services:
|
||||
oracle:
|
||||
image: ${{ matrix.version.image }}
|
||||
image: metabase/qa-databases:oracle-xe-21.3
|
||||
env:
|
||||
ORACLE_PASSWORD: password
|
||||
ports:
|
||||
- "1521:${{ matrix.version.port }}"
|
||||
name: ${{ matrix.version.name }}
|
||||
- "2484:2484"
|
||||
name: Oracle 21.3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }}
|
||||
- name: Test Oracle 21.3
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: ${{ matrix.version.junit-name }}
|
||||
junit-name: 'be-tests-oracle-21-3-ee'
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
@@ -682,46 +636,10 @@ jobs:
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-postgres:
|
||||
if: ${{ !inputs.skip }}
|
||||
be-tests-postgres-latest:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- name: Postgres 12.x
|
||||
junit-name: postgres-ee
|
||||
docker-image: postgres:12-alpine
|
||||
env:
|
||||
enable-ssl-tests: 'false'
|
||||
- name: Postgres Latest
|
||||
junit-name: postgres-latest-ee
|
||||
docker-image: postgres:latest
|
||||
env:
|
||||
enable-ssl-tests: 'true'
|
||||
job:
|
||||
- name: Driver Tests
|
||||
test-args: >-
|
||||
:only-tags [:mb/driver-tests]
|
||||
name: "${{ matrix.version.name }} ${{ matrix.job.name }}"
|
||||
env:
|
||||
# Only run old migrations tests on pushes to master or release branches. All other branches should skip tests
|
||||
# with the tag `mb/old-migrations-test`. `__ADDITIONAL_EXCLUDED_TAG__` is not used anywhere outside of splicing
|
||||
# it in to the command below.
|
||||
__ADDITIONAL_EXCLUDED_TAG__: >-
|
||||
${{
|
||||
(
|
||||
github.event_name == 'push' &&
|
||||
(
|
||||
github.ref == 'master' ||
|
||||
startsWith(github.ref, 'release-')
|
||||
) &&
|
||||
''
|
||||
) ||
|
||||
':mb/old-migrations-test'
|
||||
}}
|
||||
# actual serious env vars below
|
||||
CI: 'true'
|
||||
DRIVERS: postgres
|
||||
MB_DB_TYPE: postgres
|
||||
@@ -730,28 +648,28 @@ jobs:
|
||||
MB_DB_DBNAME: mb_test
|
||||
MB_DB_USER: mb_test
|
||||
MB_POSTGRESQL_TEST_USER: mb_test
|
||||
# SSL tests are only enabled for the postgres-latest job.
|
||||
MB_POSTGRES_SSL_TEST_SSL: ${{ matrix.version.env.enable-ssl-tests }}
|
||||
# SSL tests enabled for postgres latest
|
||||
MB_POSTGRES_SSL_TEST_SSL: 'true'
|
||||
MB_POSTGRES_SSL_TEST_SSL_MODE: verify-full
|
||||
MB_POSTGRES_SSL_TEST_SSL_ROOT_CERT_PATH: 'test-resources/certificates/us-east-2-bundle.pem'
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ matrix.version.docker-image }}
|
||||
image: postgres:latest
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env:
|
||||
POSTGRES_USER: mb_test
|
||||
POSTGRES_DB: mb_test
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
name: PostgreSQL Latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test ${{ matrix.version.name }} (${{ matrix.job.name }})
|
||||
- name: Test PostgreSQL Latest
|
||||
uses: ./.github/actions/test-driver
|
||||
with:
|
||||
junit-name: 'be-tests-${{ matrix.version.junit-name }}'
|
||||
junit-name: 'be-tests-postgres-latest-ee'
|
||||
test-args: >-
|
||||
${{ matrix.job.test-args }}
|
||||
:exclude-tags '[ ${{ matrix.job.exclude-tag }} ${{ env.__ADDITIONAL_EXCLUDED_TAG__ }}]'
|
||||
:only-tags [:mb/driver-tests]
|
||||
- name: Upload Test Results
|
||||
uses: ./.github/actions/upload-test-results
|
||||
if: always()
|
||||
@@ -766,7 +684,7 @@ jobs:
|
||||
|
||||
be-tests-presto-jdbc-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -834,7 +752,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-redshift-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
@@ -884,7 +801,6 @@ jobs:
|
||||
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
|
||||
be-tests-snowflake-ee:
|
||||
if: ${{ !inputs.skip }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -928,7 +844,7 @@ jobs:
|
||||
|
||||
be-tests-sparksql-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -962,7 +878,7 @@ jobs:
|
||||
|
||||
be-tests-sqlite-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -991,7 +907,7 @@ jobs:
|
||||
|
||||
be-tests-sqlserver:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
@@ -1042,7 +958,7 @@ jobs:
|
||||
|
||||
be-tests-starburst-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -1080,7 +996,7 @@ jobs:
|
||||
|
||||
be-tests-vertica-ee:
|
||||
needs: determine-driver-skips
|
||||
if: ${{ !inputs.skip && !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
if: ${{ !(needs.determine-driver-skips.outputs.skip == 'true') }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
@@ -1122,13 +1038,15 @@ jobs:
|
||||
- be-tests-bigquery-cloud-sdk-ee
|
||||
- be-tests-druid-ee
|
||||
- be-tests-databricks-ee
|
||||
- be-tests-databricks-multi-catalog-ee
|
||||
- be-tests-druid-jdbc-ee
|
||||
- be-tests-mysql-mariadb
|
||||
- be-tests-mysql-latest
|
||||
- be-tests-mariadb-latest
|
||||
- be-tests-mongo
|
||||
- be-tests-mongo-ssl
|
||||
- be-tests-mongo-sharded-cluster-ee
|
||||
- be-tests-oracle
|
||||
- be-tests-postgres
|
||||
- be-tests-oracle-21
|
||||
- be-tests-postgres-latest
|
||||
- be-tests-presto-jdbc-ee
|
||||
- be-tests-redshift-ee
|
||||
- be-tests-snowflake-ee
|
||||
|
||||
22
.github/workflows/run-tests.yml
vendored
22
.github/workflows/run-tests.yml
vendored
@@ -90,17 +90,27 @@ jobs:
|
||||
|
||||
app-db-tests:
|
||||
needs: files-changed
|
||||
if: ${{ needs.files-changed.outputs.backend_all == 'true' }}
|
||||
uses: ./.github/workflows/app-db.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
driver-tests:
|
||||
driver-tests-tier1:
|
||||
needs: files-changed
|
||||
uses: ./.github/workflows/drivers.yml
|
||||
if: ${{ needs.files-changed.outputs.backend_all == 'true' }}
|
||||
uses: ./.github/workflows/drivers-tier1.yml
|
||||
secrets: inherit
|
||||
|
||||
driver-tests-tier2:
|
||||
needs: files-changed
|
||||
if: ${{ needs.files-changed.outputs.backend_all == 'true' }}
|
||||
uses: ./.github/workflows/drivers-tier2.yml
|
||||
secrets: inherit
|
||||
|
||||
driver-tests-tier3:
|
||||
needs: files-changed
|
||||
if: ${{ needs.files-changed.outputs.backend_all == 'true' }}
|
||||
uses: ./.github/workflows/drivers-tier3.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
skip: ${{ needs.files-changed.outputs.backend_all != 'true' }}
|
||||
|
||||
frontend-tests:
|
||||
needs: files-changed
|
||||
|
||||
@@ -34,7 +34,7 @@ export function trackTransformCreate({
|
||||
creationType,
|
||||
}: {
|
||||
triggeredFrom: "transform-page-create-menu";
|
||||
creationType: "query" | "native" | "saved-question";
|
||||
creationType: "query" | "native" | "python" | "saved-question";
|
||||
}) {
|
||||
trackSimpleEvent({
|
||||
event: "transform_create",
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { useState } from "react";
|
||||
import { t } from "ttag";
|
||||
|
||||
import { useListDatabasesQuery } from "metabase/api";
|
||||
import { LoadingAndErrorWrapper } from "metabase/common/components/LoadingAndErrorWrapper";
|
||||
import { Box, Select, Stack, Text } from "metabase/ui";
|
||||
import type { Database } from "metabase-types/api";
|
||||
|
||||
type PythonDataPickerProps = {
|
||||
database?: number;
|
||||
onChange: (database: number, table?: number) => void;
|
||||
};
|
||||
|
||||
export function PythonDataPicker({
|
||||
database,
|
||||
onChange,
|
||||
}: PythonDataPickerProps) {
|
||||
const [selectedDatabaseId, setSelectedDatabaseId] = useState<
|
||||
number | undefined
|
||||
>(database);
|
||||
|
||||
const {
|
||||
data: databasesResponse,
|
||||
isLoading: isLoadingDatabases,
|
||||
error: databaseError,
|
||||
} = useListDatabasesQuery();
|
||||
|
||||
if (databaseError) {
|
||||
return <LoadingAndErrorWrapper error={databaseError} />;
|
||||
}
|
||||
|
||||
if (isLoadingDatabases) {
|
||||
return <LoadingAndErrorWrapper loading />;
|
||||
}
|
||||
|
||||
const databases = databasesResponse?.data || [];
|
||||
const databaseOptions = databases.map((db: Database) => ({
|
||||
value: db.id.toString(),
|
||||
label: db.name,
|
||||
}));
|
||||
|
||||
const handleDatabaseChange = (value: string | null) => {
|
||||
const dbId = value ? parseInt(value) : undefined;
|
||||
setSelectedDatabaseId(dbId);
|
||||
if (dbId) {
|
||||
onChange(dbId, undefined); // Clear table selection when database changes
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Text size="sm" fw="bold" mb="xs">
|
||||
{t`Source Database`}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" mb="sm">
|
||||
{t`Select the database that contains your source data`}
|
||||
</Text>
|
||||
<Select
|
||||
data={databaseOptions}
|
||||
value={selectedDatabaseId?.toString() || null}
|
||||
onChange={handleDatabaseChange}
|
||||
placeholder={t`Select a database`}
|
||||
clearable
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./PythonDataPicker";
|
||||
@@ -0,0 +1,112 @@
|
||||
import { useHotkeys } from "@mantine/hooks";
|
||||
import { useState } from "react";
|
||||
|
||||
import { Flex } from "metabase/ui";
|
||||
import type { TransformSource } from "metabase-types/api";
|
||||
|
||||
import { PythonDataPicker } from "../PythonDataPicker";
|
||||
import { EditorHeader } from "../QueryEditor/EditorHeader";
|
||||
import { PythonEditor } from "../QueryEditor/PythonEditor";
|
||||
|
||||
type PythonTransformEditorProps = {
|
||||
initialSource: TransformSource & { type: "python" };
|
||||
isNew?: boolean;
|
||||
isSaving?: boolean;
|
||||
onSave: (newSource: TransformSource & { type: "python" }) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
export function PythonTransformEditor({
|
||||
initialSource,
|
||||
isNew = true,
|
||||
isSaving = false,
|
||||
onSave,
|
||||
onCancel,
|
||||
}: PythonTransformEditorProps) {
|
||||
const [source, setSource] = useState(initialSource);
|
||||
const [isSourceDirty, setIsSourceDirty] = useState(false);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
|
||||
const handleScriptChange = (script: string) => {
|
||||
const newSource = {
|
||||
...source,
|
||||
script,
|
||||
};
|
||||
setSource(newSource);
|
||||
setIsSourceDirty(true);
|
||||
};
|
||||
|
||||
const handleDataChange = (database: number, table?: number) => {
|
||||
const newSource = {
|
||||
...source,
|
||||
database,
|
||||
table,
|
||||
};
|
||||
setSource(newSource);
|
||||
setIsSourceDirty(true);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onSave(source);
|
||||
};
|
||||
|
||||
const handleRunScript = async () => {
|
||||
setIsRunning(true);
|
||||
// TODO: Implement Python script execution
|
||||
// For now, just simulate running
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
setIsRunning(false);
|
||||
};
|
||||
|
||||
const handleCancelScript = () => {
|
||||
setIsRunning(false);
|
||||
};
|
||||
|
||||
const handleCmdEnter = () => {
|
||||
if (isRunning) {
|
||||
handleCancelScript();
|
||||
} else {
|
||||
handleRunScript();
|
||||
}
|
||||
};
|
||||
|
||||
useHotkeys([["mod+Enter", handleCmdEnter]], []);
|
||||
|
||||
const canSave = Boolean(source.script.trim() && source.database);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
w="100%"
|
||||
h="100%"
|
||||
direction="column"
|
||||
bg="bg-white"
|
||||
data-testid="python-transform-editor"
|
||||
>
|
||||
<EditorHeader
|
||||
isNew={isNew}
|
||||
isSaving={isSaving}
|
||||
canSave={canSave && (isNew || isSourceDirty)}
|
||||
onSave={handleSave}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
<Flex direction="column" style={{ flex: 1 }} p="md">
|
||||
<PythonDataPicker
|
||||
database={source.database}
|
||||
onChange={handleDataChange}
|
||||
/>
|
||||
|
||||
<Flex direction="column" style={{ flex: 1, marginTop: "1rem" }}>
|
||||
<PythonEditor
|
||||
script={source.script}
|
||||
isRunnable={true}
|
||||
isRunning={isRunning}
|
||||
isResultDirty={false}
|
||||
onChange={handleScriptChange}
|
||||
onRunScript={handleRunScript}
|
||||
onCancelScript={handleCancelScript}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { PythonTransformEditor } from "./PythonTransformEditor";
|
||||
@@ -7,6 +7,9 @@ import { Notebook } from "metabase/querying/notebook/components/Notebook";
|
||||
import { Box } from "metabase/ui";
|
||||
import type Question from "metabase-lib/v1/Question";
|
||||
import type NativeQuery from "metabase-lib/v1/queries/NativeQuery";
|
||||
import type { TransformSource } from "metabase-types/api";
|
||||
|
||||
import { PythonEditor } from "../PythonEditor";
|
||||
|
||||
import S from "./EditorBody.module.css";
|
||||
import { ResizableBoxHandle } from "./ResizableBoxHandle";
|
||||
@@ -16,10 +19,13 @@ const EDITOR_HEIGHT = 400;
|
||||
type EditorBodyProps = {
|
||||
question: Question;
|
||||
isNative: boolean;
|
||||
isPython?: boolean;
|
||||
pythonSource?: TransformSource & { type: "python" };
|
||||
isRunnable: boolean;
|
||||
isRunning: boolean;
|
||||
isResultDirty: boolean;
|
||||
onChange: (newQuestion: Question) => void;
|
||||
onPythonChange?: (script: string) => void;
|
||||
onRunQuery: () => Promise<void>;
|
||||
onCancelQuery: () => void;
|
||||
};
|
||||
@@ -27,10 +33,13 @@ type EditorBodyProps = {
|
||||
export function EditorBody({
|
||||
question,
|
||||
isNative,
|
||||
isPython = false,
|
||||
pythonSource,
|
||||
isRunnable,
|
||||
isRunning,
|
||||
isResultDirty,
|
||||
onChange,
|
||||
onPythonChange,
|
||||
onRunQuery,
|
||||
onCancelQuery,
|
||||
}: EditorBodyProps) {
|
||||
@@ -61,6 +70,24 @@ export function EditorBody({
|
||||
onChange(newNativeQuery.question());
|
||||
};
|
||||
|
||||
const handlePythonChange = (script: string) => {
|
||||
onPythonChange?.(script);
|
||||
};
|
||||
|
||||
if (isPython) {
|
||||
return (
|
||||
<PythonEditor
|
||||
script={pythonSource?.script || ""}
|
||||
isRunnable={isRunnable}
|
||||
isRunning={isRunning}
|
||||
isResultDirty={isResultDirty}
|
||||
onChange={handlePythonChange}
|
||||
onRunScript={onRunQuery}
|
||||
onCancelScript={onCancelQuery}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return isNative ? (
|
||||
<NativeQueryEditor
|
||||
question={question}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { t } from "ttag";
|
||||
|
||||
import { CodeEditor } from "metabase/common/components/CodeEditor";
|
||||
import { Box, Button, Group, Text } from "metabase/ui";
|
||||
|
||||
type PythonEditorProps = {
|
||||
script: string;
|
||||
isRunnable: boolean;
|
||||
isRunning: boolean;
|
||||
isResultDirty: boolean;
|
||||
onChange: (script: string) => void;
|
||||
onRunScript: () => Promise<void>;
|
||||
onCancelScript: () => void;
|
||||
};
|
||||
|
||||
export function PythonEditor({
|
||||
script,
|
||||
isRunnable,
|
||||
isRunning,
|
||||
isResultDirty,
|
||||
onChange,
|
||||
onRunScript,
|
||||
onCancelScript,
|
||||
}: PythonEditorProps) {
|
||||
const handleScriptChange = (newScript: string) => {
|
||||
onChange(newScript);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box h="100%" style={{ display: "flex", flexDirection: "column" }}>
|
||||
{/* Python Script Editor */}
|
||||
<Box style={{ flex: 1 }}>
|
||||
<CodeEditor
|
||||
value={script}
|
||||
onChange={handleScriptChange}
|
||||
language="python"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<Box mt="md">
|
||||
<Group gap="sm">
|
||||
<Button
|
||||
variant="filled"
|
||||
leftSection={isRunning ? undefined : <span>▶</span>}
|
||||
onClick={isRunning ? onCancelScript : onRunScript}
|
||||
disabled={!isRunnable && !isRunning}
|
||||
loading={isRunning}
|
||||
>
|
||||
{isRunning ? t`Cancel` : t`Run Python Script`}
|
||||
</Button>
|
||||
{isResultDirty && (
|
||||
<Text size="sm" c="dimmed">
|
||||
{t`Results may be out of date`}
|
||||
</Text>
|
||||
)}
|
||||
</Group>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { PythonEditor } from "./PythonEditor";
|
||||
@@ -9,6 +9,7 @@ import { hasPremiumFeature } from "metabase-enterprise/settings";
|
||||
import { JobListPage } from "./pages/JobListPage";
|
||||
import { JobPage } from "./pages/JobPage";
|
||||
import { NewJobPage } from "./pages/NewJobPage";
|
||||
import { NewPythonTransformPage } from "./pages/NewPythonTransformPage";
|
||||
import { NewTransformQueryPage } from "./pages/NewTransformQueryPage";
|
||||
import { RunListPage } from "./pages/RunListPage";
|
||||
import { TransformListPage } from "./pages/TransformListPage";
|
||||
@@ -32,7 +33,16 @@ if (hasPremiumFeature("transforms")) {
|
||||
<Route path="runs" component={RunListPage} />
|
||||
<Route path=":transformId" component={TransformPage} />
|
||||
</Route>
|
||||
<Route path="new/:type" component={NewTransformQueryPage} />
|
||||
<Route
|
||||
path="new/:type"
|
||||
component={(props: any) =>
|
||||
props.params.type === "python" ? (
|
||||
<NewPythonTransformPage />
|
||||
) : (
|
||||
<NewTransformQueryPage params={props.params} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Route path="new/card/:cardId" component={NewTransformQueryPage} />
|
||||
<Route path=":transformId/query" component={TransformQueryPage} />
|
||||
</Route>
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import { t } from "ttag";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { Button, Group, Modal, Stack, TextInput } from "metabase/ui";
|
||||
import { useCreateTransformMutation } from "metabase-enterprise/api";
|
||||
import type { Transform, TransformSource } from "metabase-types/api";
|
||||
|
||||
type CreatePythonTransformModalProps = {
|
||||
source: TransformSource & { type: "python" };
|
||||
onCreate: (transform: Transform) => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
name: string;
|
||||
description: string | null;
|
||||
targetSchema: string | null;
|
||||
targetName: string;
|
||||
};
|
||||
|
||||
const getValidationSchema = () =>
|
||||
Yup.object({
|
||||
name: Yup.string().required(t`Name is required`),
|
||||
description: Yup.string().nullable(),
|
||||
targetSchema: Yup.string().nullable(),
|
||||
targetName: Yup.string().required(t`Table name is required`),
|
||||
});
|
||||
|
||||
export function CreatePythonTransformModal({
|
||||
source,
|
||||
onCreate,
|
||||
onClose,
|
||||
}: CreatePythonTransformModalProps) {
|
||||
const [createTransform] = useCreateTransformMutation();
|
||||
|
||||
const initialValues: FormValues = {
|
||||
name: "",
|
||||
description: null,
|
||||
targetSchema: "python_transforms",
|
||||
targetName: "",
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: FormValues) => {
|
||||
try {
|
||||
const transform = await createTransform({
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
source,
|
||||
target: {
|
||||
type: "table",
|
||||
schema: values.targetSchema,
|
||||
name: values.targetName,
|
||||
},
|
||||
}).unwrap();
|
||||
|
||||
onCreate(transform);
|
||||
} catch (error) {
|
||||
console.error("Error creating Python transform:", error);
|
||||
// TODO: Show error toast
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
opened
|
||||
onClose={onClose}
|
||||
title={t`Create Python Transform`}
|
||||
size="md"
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={getValidationSchema()}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ isSubmitting, isValid }) => (
|
||||
<Form>
|
||||
<Stack gap="md">
|
||||
<Field name="name">
|
||||
{({ field, meta }: any) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t`Transform name`}
|
||||
placeholder={t`My Python Transform`}
|
||||
error={meta.touched && meta.error}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="description">
|
||||
{({ field }: any) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t`Description (optional)`}
|
||||
placeholder={t`What does this transform do?`}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="targetName">
|
||||
{({ field, meta }: any) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t`Output table name`}
|
||||
placeholder={t`my_python_transform`}
|
||||
error={meta.touched && meta.error}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="targetSchema">
|
||||
{({ field }: any) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t`Schema (optional)`}
|
||||
placeholder={t`python_transforms`}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button variant="subtle" onClick={onClose}>
|
||||
{t`Cancel`}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
disabled={!isValid}
|
||||
>
|
||||
{t`Create transform`}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { CreatePythonTransformModal } from "./CreatePythonTransformModal";
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { useState } from "react";
|
||||
import { push } from "react-router-redux";
|
||||
|
||||
import { useDispatch } from "metabase/lib/redux";
|
||||
import type { Transform, TransformSource } from "metabase-types/api";
|
||||
|
||||
import { PythonTransformEditor } from "../../components/PythonTransformEditor";
|
||||
import { getTransformListUrl, getTransformUrl } from "../../urls";
|
||||
|
||||
import { CreatePythonTransformModal } from "./CreatePythonTransformModal";
|
||||
|
||||
const DEFAULT_PYTHON_SOURCE: TransformSource & { type: "python" } = {
|
||||
type: "python",
|
||||
database: 1, // Default to first database, will be updated by user
|
||||
table: undefined,
|
||||
script: `# Write your Python transformation script here
|
||||
import pandas as pd
|
||||
|
||||
def transform(data):
|
||||
"""
|
||||
Your transformation function.
|
||||
|
||||
Args:
|
||||
data: Input data (to be defined based on implementation)
|
||||
|
||||
Returns:
|
||||
Transformed data
|
||||
"""
|
||||
# Your transformation logic here
|
||||
return data
|
||||
|
||||
# Example usage:
|
||||
# result = transform(input_data)`,
|
||||
};
|
||||
|
||||
export function NewPythonTransformPage() {
|
||||
const [source, setSource] = useState<TransformSource & { type: "python" }>(
|
||||
DEFAULT_PYTHON_SOURCE,
|
||||
);
|
||||
const [isModalOpened, { open: openModal, close: closeModal }] =
|
||||
useDisclosure();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleCreate = (transform: Transform) => {
|
||||
dispatch(push(getTransformUrl(transform.id)));
|
||||
};
|
||||
|
||||
const handleSaveClick = (newSource: TransformSource & { type: "python" }) => {
|
||||
setSource(newSource);
|
||||
openModal();
|
||||
};
|
||||
|
||||
const handleCancelClick = () => {
|
||||
dispatch(push(getTransformListUrl()));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PythonTransformEditor
|
||||
initialSource={DEFAULT_PYTHON_SOURCE}
|
||||
isNew
|
||||
onSave={handleSaveClick}
|
||||
onCancel={handleCancelClick}
|
||||
/>
|
||||
{isModalOpened && (
|
||||
<CreatePythonTransformModal
|
||||
source={source}
|
||||
onCreate={handleCreate}
|
||||
onClose={closeModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { NewPythonTransformPage } from "./NewPythonTransformPage";
|
||||
@@ -90,7 +90,7 @@ function getParsedParams({
|
||||
cardId,
|
||||
}: NewTransformQueryPageParams): NewTransformQueryPageParsedParams {
|
||||
return {
|
||||
type: type === "native" ? "native" : "query",
|
||||
type: type === "native" ? "native" : type === "python" ? "query" : "query",
|
||||
cardId: cardId != null ? Urls.extractEntityId(cardId) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,6 +72,19 @@ export function CreateTransformMenu() {
|
||||
>
|
||||
{t`SQL query`}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
component={ForwardRefLink}
|
||||
to={getNewTransformFromTypeUrl("python")}
|
||||
leftSection={<Icon name="code_block" />}
|
||||
onClick={() => {
|
||||
trackTransformCreate({
|
||||
triggeredFrom: "transform-page-create-menu",
|
||||
creationType: "python",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t`Python script`}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<Icon name="folder" />}
|
||||
onClick={handleSavedQuestionClick}
|
||||
|
||||
@@ -19,9 +19,12 @@ type ManageSectionProps = {
|
||||
};
|
||||
|
||||
export function ManageSection({ transform }: ManageSectionProps) {
|
||||
const sectionLabel =
|
||||
transform.source.type === "python" ? t`Python Script` : t`Query`;
|
||||
|
||||
return (
|
||||
<TitleSection
|
||||
label={t`Query`}
|
||||
label={sectionLabel}
|
||||
rightSection={
|
||||
<Group>
|
||||
<EditQueryButton transform={transform} />
|
||||
@@ -30,7 +33,22 @@ export function ManageSection({ transform }: ManageSectionProps) {
|
||||
}
|
||||
>
|
||||
<Card p={0} shadow="none" withBorder>
|
||||
<QueryView query={transform.source.query} />
|
||||
{transform.source.type === "query" && (
|
||||
<QueryView query={transform.source.query} />
|
||||
)}
|
||||
{transform.source.type === "python" && (
|
||||
<div style={{ padding: "1rem" }}>
|
||||
<p>
|
||||
<strong>{t`Database ID`}:</strong> {transform.source.database}
|
||||
</p>
|
||||
{transform.source.table && (
|
||||
<p>
|
||||
<strong>{t`Table ID`}:</strong> {transform.source.table}
|
||||
</p>
|
||||
)}
|
||||
<pre>{transform.source.script}</pre>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</TitleSection>
|
||||
);
|
||||
@@ -41,13 +59,16 @@ type EditQueryButtonProps = {
|
||||
};
|
||||
|
||||
function EditQueryButton({ transform }: EditQueryButtonProps) {
|
||||
const buttonText =
|
||||
transform.source.type === "python" ? t`Edit script` : t`Edit query`;
|
||||
|
||||
return (
|
||||
<Button
|
||||
component={Link}
|
||||
to={getTransformQueryUrl(transform.id)}
|
||||
leftSection={<Icon name="pencil_lines" aria-hidden />}
|
||||
>
|
||||
{t`Edit query`}
|
||||
{buttonText}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,12 @@ type TargetInfoProps = {
|
||||
|
||||
function TargetInfo({ transform }: TargetInfoProps) {
|
||||
const { source, target, table } = transform;
|
||||
const { database: databaseId } = source.query;
|
||||
const databaseId =
|
||||
source.type === "query"
|
||||
? source.query.database
|
||||
: source.type === "python"
|
||||
? source.database
|
||||
: undefined;
|
||||
const { data, isLoading } = useGetDatabaseQuery(
|
||||
table == null && databaseId != null ? { id: databaseId } : skipToken,
|
||||
);
|
||||
|
||||
@@ -78,7 +78,12 @@ function UpdateTargetForm({
|
||||
onClose,
|
||||
}: UpdateTargetFormProps) {
|
||||
const { source, target, table } = transform;
|
||||
const { database: databaseId } = source.query;
|
||||
const databaseId =
|
||||
source.type === "query"
|
||||
? source.query.database
|
||||
: source.type === "python"
|
||||
? source.database
|
||||
: undefined;
|
||||
const [updateTransform] = useUpdateTransformMutation();
|
||||
const [deleteTransformTarget] = useDeleteTransformTargetMutation();
|
||||
const initialValues = useMemo(() => getInitialValues(transform), [transform]);
|
||||
|
||||
@@ -53,6 +53,13 @@ export function TransformQueryPageBody({
|
||||
const dispatch = useDispatch();
|
||||
const { sendErrorToast, sendSuccessToast } = useMetadataToasts();
|
||||
|
||||
if (transform.source.type === "python") {
|
||||
// For Python transforms, redirect to the main transform page
|
||||
// where they can edit the script
|
||||
dispatch(push(getTransformUrl(transform.id)));
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleSave = async (query: DatasetQuery) => {
|
||||
const { error } = await updateTransform({
|
||||
id: transform.id,
|
||||
|
||||
@@ -15,7 +15,9 @@ export function getTransformListUrl() {
|
||||
return ROOT_URL;
|
||||
}
|
||||
|
||||
export function getNewTransformFromTypeUrl(type: DatasetQuery["type"]) {
|
||||
export function getNewTransformFromTypeUrl(
|
||||
type: DatasetQuery["type"] | "python",
|
||||
) {
|
||||
return `${ROOT_URL}/new/${type}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ export type TransformJobTriggerManualRunEvent = ValidateEvent<{
|
||||
export type TransformCreateEvent = ValidateEvent<{
|
||||
event: "transform_create";
|
||||
triggered_from: "transform-page-create-menu";
|
||||
event_detail: "query" | "native" | "saved-question";
|
||||
event_detail: "query" | "native" | "python" | "saved-question";
|
||||
}>;
|
||||
|
||||
export type TransformCreatedEvent = ValidateEvent<{
|
||||
|
||||
@@ -9,13 +9,19 @@ import type {
|
||||
|
||||
import { createMockStructuredDatasetQuery } from "./query";
|
||||
|
||||
export function createMockTransformSource(
|
||||
opts?: Partial<TransformSource>,
|
||||
): TransformSource {
|
||||
export function createMockTransformSource(): TransformSource {
|
||||
return {
|
||||
type: "query",
|
||||
query: createMockStructuredDatasetQuery(),
|
||||
...opts,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMockPythonTransformSource(): TransformSource {
|
||||
return {
|
||||
type: "python",
|
||||
script: "# Python script\nprint('Hello, world!')",
|
||||
database: 1,
|
||||
table: 1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,17 @@ export type Transform = {
|
||||
last_run?: TransformRun | null;
|
||||
};
|
||||
|
||||
export type TransformSource = {
|
||||
type: "query";
|
||||
query: DatasetQuery;
|
||||
};
|
||||
export type TransformSource =
|
||||
| {
|
||||
type: "query";
|
||||
query: DatasetQuery;
|
||||
}
|
||||
| {
|
||||
type: "python";
|
||||
script: string;
|
||||
database: number;
|
||||
table?: number;
|
||||
};
|
||||
|
||||
export type TransformTargetType = "table";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user