@decaf-ts/integrations 0.3.12 → 0.4.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/integrations.cjs +445 -7
- package/dist/integrations.js +479 -8
- package/jest.config.cjs +1 -1
- package/lib/cjs/feature-flags/constants.cjs +16 -0
- package/lib/cjs/feature-flags/decorators.cjs +65 -0
- package/lib/cjs/feature-flags/environment.cjs +26 -0
- package/lib/cjs/feature-flags/index.cjs +31 -0
- package/lib/cjs/feature-flags/models/FeatureFlag.model.cjs +81 -0
- package/lib/cjs/feature-flags/models/FeatureFlagAccess.model.cjs +78 -0
- package/lib/cjs/feature-flags/readers/FeatureFlagReader.cjs +25 -0
- package/lib/cjs/feature-flags/readers/index.cjs +19 -0
- package/lib/cjs/feature-flags/services/FeatureFlag.service.cjs +259 -0
- package/lib/cjs/feature-flags/types.cjs +4 -0
- package/lib/cjs/feature-flags/utils.cjs +141 -0
- package/lib/cjs/graph/engine/execution/SwitchGraphNodeExecutor.cjs +9 -9
- package/lib/cjs/graph/shared/nodes/flow-control.cjs +4 -3
- package/lib/cjs/graph/shared/types.cjs +2 -2
- package/lib/cjs/index.cjs +6 -5
- package/lib/cjs/kibana/builders/KibanaIndexBuilder.cjs +329 -0
- package/lib/cjs/kibana/builders/KibanaIndexBuilderCollection.cjs +37 -0
- package/lib/cjs/kibana/builders/index.cjs +20 -0
- package/lib/cjs/kibana/helpers.cjs +20 -16
- package/lib/cjs/kibana/index.cjs +3 -2
- package/lib/cjs/kibana/types.cjs +11 -2
- package/lib/cjs/loader/ObjectLoader.cjs +154 -0
- package/lib/cjs/loader/families.cjs +78 -0
- package/lib/cjs/loader/index.cjs +28 -0
- package/lib/cjs/loader/types.cjs +4 -0
- package/lib/cjs/nest/decorators.cjs +17 -0
- package/lib/cjs/nest/index.cjs +3 -3
- package/lib/cjs/nest/keycloakAuthHandler.cjs +74 -35
- package/lib/cjs/nest/keycloakModule.cjs +7 -10
- package/lib/cjs/nest/types.cjs +2 -2
- package/lib/cjs/nest/utils.cjs +58 -50
- package/lib/cjs/plugins/contract.cjs +76 -0
- package/lib/cjs/plugins/index.cjs +35 -0
- package/lib/cjs/plugins/kibana/host.cjs +24 -0
- package/lib/cjs/plugins/kibana/index.cjs +38 -0
- package/lib/cjs/plugins/kibana/installer.cjs +180 -0
- package/lib/cjs/plugins/kibana/manifest.cjs +33 -0
- package/lib/cjs/plugins/kibana/templates.cjs +503 -0
- package/lib/cjs/plugins/kibana/types.cjs +33 -0
- package/lib/cjs/plugins/superset/host.cjs +26 -0
- package/lib/cjs/plugins/superset/index.cjs +40 -0
- package/lib/cjs/plugins/superset/installer.cjs +232 -0
- package/lib/cjs/plugins/superset/manifest.cjs +35 -0
- package/lib/cjs/plugins/superset/templates.cjs +844 -0
- package/lib/cjs/plugins/superset/types.cjs +39 -0
- package/lib/esm/feature-flags/constants.js +12 -0
- package/lib/esm/feature-flags/decorators.js +49 -0
- package/lib/esm/feature-flags/environment.js +18 -0
- package/lib/esm/feature-flags/index.js +14 -0
- package/lib/esm/feature-flags/models/FeatureFlag.model.js +72 -0
- package/lib/esm/feature-flags/models/FeatureFlagAccess.model.js +69 -0
- package/lib/esm/feature-flags/readers/FeatureFlagReader.js +19 -0
- package/lib/esm/feature-flags/readers/index.js +2 -0
- package/lib/esm/feature-flags/services/FeatureFlag.service.js +250 -0
- package/lib/esm/feature-flags/types.js +2 -0
- package/lib/esm/feature-flags/utils.js +126 -0
- package/lib/esm/graph/engine/execution/SwitchGraphNodeExecutor.js +8 -8
- package/lib/esm/graph/shared/nodes/flow-control.js +3 -2
- package/lib/esm/graph/shared/types.js +1 -1
- package/lib/esm/index.js +5 -4
- package/lib/esm/kibana/builders/KibanaIndexBuilder.js +319 -0
- package/lib/esm/kibana/builders/KibanaIndexBuilderCollection.js +32 -0
- package/lib/esm/kibana/builders/index.js +3 -0
- package/lib/esm/kibana/helpers.js +19 -15
- package/lib/esm/kibana/index.js +2 -1
- package/lib/esm/kibana/types.js +13 -2
- package/lib/esm/loader/ObjectLoader.js +115 -0
- package/lib/esm/loader/families.js +66 -0
- package/lib/esm/loader/index.js +11 -0
- package/lib/esm/loader/types.js +2 -0
- package/lib/esm/nest/decorators.js +12 -0
- package/lib/esm/nest/index.js +2 -2
- package/lib/esm/nest/keycloakAuthHandler.js +67 -35
- package/lib/esm/nest/keycloakModule.js +7 -10
- package/lib/esm/nest/types.js +1 -1
- package/lib/esm/nest/utils.js +55 -46
- package/lib/esm/plugins/contract.js +68 -0
- package/lib/esm/plugins/index.js +18 -0
- package/lib/esm/plugins/kibana/host.js +19 -0
- package/lib/esm/plugins/kibana/index.js +14 -0
- package/lib/esm/plugins/kibana/installer.js +140 -0
- package/lib/esm/plugins/kibana/manifest.js +32 -0
- package/lib/esm/plugins/kibana/templates.js +499 -0
- package/lib/esm/plugins/kibana/types.js +10 -0
- package/lib/esm/plugins/superset/host.js +21 -0
- package/lib/esm/plugins/superset/index.js +16 -0
- package/lib/esm/plugins/superset/installer.js +192 -0
- package/lib/esm/plugins/superset/manifest.js +30 -0
- package/lib/esm/plugins/superset/templates.js +840 -0
- package/lib/esm/plugins/superset/types.js +11 -0
- package/lib/types/feature-flags/constants.d.cts +11 -0
- package/lib/types/feature-flags/constants.d.mts +11 -0
- package/lib/types/feature-flags/decorators.d.cts +11 -0
- package/lib/types/feature-flags/decorators.d.mts +11 -0
- package/lib/types/feature-flags/environment.d.cts +11 -0
- package/lib/types/feature-flags/environment.d.mts +11 -0
- package/lib/types/feature-flags/index.d.cts +13 -0
- package/lib/types/feature-flags/index.d.mts +13 -0
- package/lib/types/feature-flags/models/FeatureFlag.model.d.cts +16 -0
- package/lib/types/feature-flags/models/FeatureFlag.model.d.mts +16 -0
- package/lib/types/feature-flags/models/FeatureFlagAccess.model.d.cts +15 -0
- package/lib/types/feature-flags/models/FeatureFlagAccess.model.d.mts +15 -0
- package/lib/types/feature-flags/readers/FeatureFlagReader.d.cts +10 -0
- package/lib/types/feature-flags/readers/FeatureFlagReader.d.mts +10 -0
- package/lib/types/feature-flags/readers/index.d.cts +1 -0
- package/lib/types/feature-flags/readers/index.d.mts +1 -0
- package/lib/types/feature-flags/services/FeatureFlag.service.d.cts +43 -0
- package/lib/types/feature-flags/services/FeatureFlag.service.d.mts +43 -0
- package/lib/types/feature-flags/types.d.cts +39 -0
- package/lib/types/feature-flags/types.d.mts +39 -0
- package/lib/types/feature-flags/utils.d.cts +13 -0
- package/lib/types/feature-flags/utils.d.mts +13 -0
- package/lib/types/graph/engine/execution/SwitchGraphNodeExecutor.d.cts +2 -2
- package/lib/types/graph/engine/execution/SwitchGraphNodeExecutor.d.mts +2 -2
- package/lib/types/graph/shared/types.d.cts +3 -3
- package/lib/types/graph/shared/types.d.mts +3 -3
- package/lib/types/index.d.cts +3 -2
- package/lib/types/index.d.mts +3 -2
- package/lib/types/kibana/builders/KibanaIndexBuilder.d.cts +67 -0
- package/lib/types/kibana/builders/KibanaIndexBuilder.d.mts +67 -0
- package/lib/types/kibana/builders/KibanaIndexBuilderCollection.d.cts +17 -0
- package/lib/types/kibana/builders/KibanaIndexBuilderCollection.d.mts +17 -0
- package/lib/types/kibana/builders/index.d.cts +2 -0
- package/lib/types/kibana/builders/index.d.mts +2 -0
- package/lib/types/kibana/index.d.cts +1 -0
- package/lib/types/kibana/index.d.mts +1 -0
- package/lib/types/kibana/types.d.cts +11 -0
- package/lib/types/kibana/types.d.mts +11 -0
- package/lib/types/loader/ObjectLoader.d.cts +27 -0
- package/lib/types/loader/ObjectLoader.d.mts +27 -0
- package/lib/types/loader/families.d.cts +34 -0
- package/lib/types/loader/families.d.mts +34 -0
- package/lib/types/loader/index.d.cts +10 -0
- package/lib/types/loader/index.d.mts +10 -0
- package/lib/types/loader/types.d.cts +24 -0
- package/lib/types/loader/types.d.mts +24 -0
- package/lib/types/nest/decorators.d.cts +8 -0
- package/lib/types/nest/decorators.d.mts +8 -0
- package/lib/types/nest/index.d.cts +1 -1
- package/lib/types/nest/index.d.mts +1 -1
- package/lib/types/nest/keycloakAuthHandler.d.cts +19 -42
- package/lib/types/nest/keycloakAuthHandler.d.mts +19 -42
- package/lib/types/nest/keycloakModule.d.cts +3 -5
- package/lib/types/nest/keycloakModule.d.mts +3 -5
- package/lib/types/nest/types.d.cts +2 -0
- package/lib/types/nest/types.d.mts +2 -0
- package/lib/types/nest/utils.d.cts +3 -4
- package/lib/types/nest/utils.d.mts +3 -4
- package/lib/types/plugins/contract.d.cts +180 -0
- package/lib/types/plugins/contract.d.mts +180 -0
- package/lib/types/plugins/index.d.cts +17 -0
- package/lib/types/plugins/index.d.mts +17 -0
- package/lib/types/plugins/kibana/host.d.cts +18 -0
- package/lib/types/plugins/kibana/host.d.mts +18 -0
- package/lib/types/plugins/kibana/index.d.cts +13 -0
- package/lib/types/plugins/kibana/index.d.mts +13 -0
- package/lib/types/plugins/kibana/installer.d.cts +45 -0
- package/lib/types/plugins/kibana/installer.d.mts +45 -0
- package/lib/types/plugins/kibana/manifest.d.cts +31 -0
- package/lib/types/plugins/kibana/manifest.d.mts +31 -0
- package/lib/types/plugins/kibana/templates.d.cts +12 -0
- package/lib/types/plugins/kibana/templates.d.mts +12 -0
- package/lib/types/plugins/kibana/types.d.cts +9 -0
- package/lib/types/plugins/kibana/types.d.mts +9 -0
- package/lib/types/plugins/superset/host.d.cts +20 -0
- package/lib/types/plugins/superset/host.d.mts +20 -0
- package/lib/types/plugins/superset/index.d.cts +15 -0
- package/lib/types/plugins/superset/index.d.mts +15 -0
- package/lib/types/plugins/superset/installer.d.cts +70 -0
- package/lib/types/plugins/superset/installer.d.mts +70 -0
- package/lib/types/plugins/superset/manifest.d.cts +28 -0
- package/lib/types/plugins/superset/manifest.d.mts +28 -0
- package/lib/types/plugins/superset/templates.d.cts +12 -0
- package/lib/types/plugins/superset/templates.d.mts +12 -0
- package/lib/types/plugins/superset/types.d.cts +10 -0
- package/lib/types/plugins/superset/types.d.mts +10 -0
- package/package.json +62 -3
- package/plugins/kibana/.gitignore +16 -0
- package/plugins/kibana/Dockerfile +19 -0
- package/plugins/kibana/docker-compose.yml +50 -0
- package/plugins/kibana/host/index.html +101 -0
- package/plugins/kibana/setup-dashboards.sh +84 -0
- package/plugins/superset/.gitignore +17 -0
- package/plugins/superset/Dockerfile +79 -0
- package/plugins/superset/docker-compose.yml +62 -0
- package/plugins/superset/host/index.html +168 -0
- package/plugins/superset/setup-dashboards.sh +106 -0
- package/plugins/superset/superset_config.py +49 -0
- package/lib/cjs/nest/authService.cjs +0 -91
- package/lib/esm/nest/authService.js +0 -86
- package/lib/types/nest/authService.d.cts +0 -63
- package/lib/types/nest/authService.d.mts +0 -63
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Creates two dummy dashboards in Kibana via the saved objects API.
|
|
4
|
+
#
|
|
5
|
+
# Usage: setup-dashboards.sh <kibana_host>
|
|
6
|
+
# kibana_host e.g. http://localhost:5602
|
|
7
|
+
#
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
KIBANA_HOST=${1:?Usage: setup-dashboards.sh <kibana_host>}
|
|
11
|
+
|
|
12
|
+
echo "Waiting for Kibana to be ready..."
|
|
13
|
+
for i in $(seq 1 60); do
|
|
14
|
+
if curl -sf "${KIBANA_HOST}/api/status" >/dev/null 2>&1; then
|
|
15
|
+
echo "Kibana is ready."
|
|
16
|
+
break
|
|
17
|
+
fi
|
|
18
|
+
sleep 2
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
# Create an index pattern first (required for dashboards)
|
|
22
|
+
echo "Creating index pattern..."
|
|
23
|
+
INDEX_PATTERN_RESPONSE=$(curl -sf -X POST "${KIBANA_HOST}/api/saved_objects/index-pattern" \
|
|
24
|
+
-H "kbn-xsrf: true" \
|
|
25
|
+
-H "Content-Type: application/json" \
|
|
26
|
+
-d '{
|
|
27
|
+
"attributes": {
|
|
28
|
+
"title": "test-logs-*",
|
|
29
|
+
"timeFieldName": "@timestamp"
|
|
30
|
+
}
|
|
31
|
+
}')
|
|
32
|
+
|
|
33
|
+
INDEX_PATTERN_ID=$(echo "$INDEX_PATTERN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
|
34
|
+
echo "Created index pattern: $INDEX_PATTERN_ID"
|
|
35
|
+
|
|
36
|
+
# Create Dashboard A
|
|
37
|
+
echo "Creating Dashboard A..."
|
|
38
|
+
DASHBOARD_A_RESPONSE=$(curl -sf -X POST "${KIBANA_HOST}/api/saved_objects/dashboard" \
|
|
39
|
+
-H "kbn-xsrf: true" \
|
|
40
|
+
-H "Content-Type: application/json" \
|
|
41
|
+
-d "{
|
|
42
|
+
\"attributes\": {
|
|
43
|
+
\"title\": \"E2E Test Dashboard A\",
|
|
44
|
+
\"description\": \"Dashboard A for e2e testing\",
|
|
45
|
+
\"panelsJSON\": \"[]\",
|
|
46
|
+
\"optionsJSON\": \"{\\\"hidePanelTitles\\\":false,\\\"useMargins\\\":true}\",
|
|
47
|
+
\"version\": 3,
|
|
48
|
+
\"timeRestore\": false
|
|
49
|
+
}
|
|
50
|
+
}")
|
|
51
|
+
|
|
52
|
+
DASHBOARD_A_ID=$(echo "$DASHBOARD_A_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
|
53
|
+
echo "Created Dashboard A: $DASHBOARD_A_ID"
|
|
54
|
+
|
|
55
|
+
# Create Dashboard B
|
|
56
|
+
echo "Creating Dashboard B..."
|
|
57
|
+
DASHBOARD_B_RESPONSE=$(curl -sf -X POST "${KIBANA_HOST}/api/saved_objects/dashboard" \
|
|
58
|
+
-H "kbn-xsrf: true" \
|
|
59
|
+
-H "Content-Type: application/json" \
|
|
60
|
+
-d "{
|
|
61
|
+
\"attributes\": {
|
|
62
|
+
\"title\": \"E2E Test Dashboard B\",
|
|
63
|
+
\"description\": \"Dashboard B for e2e testing\",
|
|
64
|
+
\"panelsJSON\": \"[]\",
|
|
65
|
+
\"optionsJSON\": \"{\\\"hidePanelTitles\\\":false,\\\"useMargins\\\":true}\",
|
|
66
|
+
\"version\": 3,
|
|
67
|
+
\"timeRestore\": false
|
|
68
|
+
}
|
|
69
|
+
}")
|
|
70
|
+
|
|
71
|
+
DASHBOARD_B_ID=$(echo "$DASHBOARD_B_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
|
72
|
+
echo "Created Dashboard B: $DASHBOARD_B_ID"
|
|
73
|
+
|
|
74
|
+
# Output the dashboard IDs for the test harness
|
|
75
|
+
cat > "$(dirname "$0")/dashboards.json" << EOF
|
|
76
|
+
{
|
|
77
|
+
"indexPatternId": "$INDEX_PATTERN_ID",
|
|
78
|
+
"dashboardA": "$DASHBOARD_A_ID",
|
|
79
|
+
"dashboardB": "$DASHBOARD_B_ID"
|
|
80
|
+
}
|
|
81
|
+
EOF
|
|
82
|
+
|
|
83
|
+
echo "Dashboard setup complete."
|
|
84
|
+
cat "$(dirname "$0")/dashboards.json"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Ignore everything by default
|
|
2
|
+
*
|
|
3
|
+
|
|
4
|
+
# Re-include the .gitignore itself
|
|
5
|
+
!.gitignore
|
|
6
|
+
|
|
7
|
+
# Re-include curated configuration (Dockerfiles, compose, config, host pages, scripts)
|
|
8
|
+
!Dockerfile
|
|
9
|
+
!docker-compose.yml
|
|
10
|
+
!superset_config.py
|
|
11
|
+
!setup-dashboards.sh
|
|
12
|
+
!dashboards.json
|
|
13
|
+
!host/
|
|
14
|
+
!host/**
|
|
15
|
+
|
|
16
|
+
# Everything else (generated patch scripts org_dashboard_embed_superset/,
|
|
17
|
+
# cloned Superset source .superset-source/, build artifacts, node_modules) remains ignored
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Custom Superset image built from source with the switchable-dashboard patch.
|
|
2
|
+
#
|
|
3
|
+
# This Dockerfile clones the Apache Superset source, applies the patch scripts
|
|
4
|
+
# (materialized by SupersetDashboardEmbedPlugin.install()), builds the SDK and
|
|
5
|
+
# frontend, and produces a lean runtime image.
|
|
6
|
+
#
|
|
7
|
+
# Build context is the integrations/plugins/superset/ directory, which contains:
|
|
8
|
+
# - org_dashboard_embed_superset/ (generated by install())
|
|
9
|
+
# - superset_config.py (curated runtime config)
|
|
10
|
+
#
|
|
11
|
+
FROM node:18-slim AS builder
|
|
12
|
+
|
|
13
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
14
|
+
git python3 python3-pip curl bash \
|
|
15
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
16
|
+
|
|
17
|
+
WORKDIR /build
|
|
18
|
+
|
|
19
|
+
# Clone Superset source
|
|
20
|
+
ARG SUPERSET_GIT_URL=https://github.com/apache/superset.git
|
|
21
|
+
ARG SUPERSET_GIT_REF=6.1.0
|
|
22
|
+
RUN git clone --depth 1 --branch ${SUPERSET_GIT_REF} ${SUPERSET_GIT_URL} /build/superset
|
|
23
|
+
|
|
24
|
+
# Copy patch scripts (materialized by install())
|
|
25
|
+
COPY org_dashboard_embed_superset/patches/ /build/patches/
|
|
26
|
+
COPY org_dashboard_embed_superset/build/ /build/build-scripts/
|
|
27
|
+
|
|
28
|
+
# Apply patch
|
|
29
|
+
RUN python3 /build/patches/apply_superset_6_1_patch.py /build/superset
|
|
30
|
+
|
|
31
|
+
# Verify patch
|
|
32
|
+
RUN bash /build/patches/verify_patch.sh /build/superset
|
|
33
|
+
|
|
34
|
+
# Build SDK
|
|
35
|
+
RUN cd /build/superset/superset-embedded-sdk && npm ci && npm run build
|
|
36
|
+
|
|
37
|
+
# Build frontend
|
|
38
|
+
RUN cd /build/superset/superset-frontend && npm ci && npm run build
|
|
39
|
+
|
|
40
|
+
# ─── Runtime stage ──────────────────────────────────────────────────────
|
|
41
|
+
FROM node:18-slim AS runtime
|
|
42
|
+
|
|
43
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
44
|
+
python3 python3-pip python3-venv curl bash \
|
|
45
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
46
|
+
|
|
47
|
+
RUN python3 -m venv /app/venv
|
|
48
|
+
ENV PATH="/app/venv/bin:$PATH"
|
|
49
|
+
|
|
50
|
+
WORKDIR /app
|
|
51
|
+
|
|
52
|
+
# Install Superset Python dependencies
|
|
53
|
+
RUN pip install --no-cache-dir \
|
|
54
|
+
apache-superset==6.1.0 \
|
|
55
|
+
psycopg2-binary \
|
|
56
|
+
celery \
|
|
57
|
+
redis \
|
|
58
|
+
gunicorn
|
|
59
|
+
|
|
60
|
+
# Copy built frontend assets from builder
|
|
61
|
+
COPY --from=builder /build/superset/superset-frontend/ /app/superset-frontend/
|
|
62
|
+
|
|
63
|
+
# Copy built SDK
|
|
64
|
+
COPY --from=builder /build/superset/superset-embedded-sdk/ /app/superset-embedded-sdk/
|
|
65
|
+
|
|
66
|
+
# Copy Superset config
|
|
67
|
+
COPY superset_config.py /app/
|
|
68
|
+
|
|
69
|
+
ENV SUPERSET_CONFIG_PATH=/app/superset_config.py
|
|
70
|
+
ENV FLASK_APP=superset
|
|
71
|
+
|
|
72
|
+
EXPOSE 8088
|
|
73
|
+
|
|
74
|
+
# Initialize Superset
|
|
75
|
+
RUN superset db upgrade && \
|
|
76
|
+
superset fab create-admin --username admin --firstname Admin --lastname User --email admin@example.com --password admin || true && \
|
|
77
|
+
superset init
|
|
78
|
+
|
|
79
|
+
CMD ["gunicorn", "-b", "0.0.0.0:8088", "-w", "4", "--timeout", "120", "superset.app:create_app()"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16-alpine
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_DB: superset
|
|
6
|
+
POSTGRES_USER: superset
|
|
7
|
+
POSTGRES_PASSWORD: superset
|
|
8
|
+
ports:
|
|
9
|
+
- "15433:5432"
|
|
10
|
+
healthcheck:
|
|
11
|
+
test: ["CMD-SHELL", "pg_isready -U superset"]
|
|
12
|
+
interval: 5s
|
|
13
|
+
timeout: 5s
|
|
14
|
+
retries: 10
|
|
15
|
+
networks:
|
|
16
|
+
- e2e
|
|
17
|
+
|
|
18
|
+
redis:
|
|
19
|
+
image: redis:7-alpine
|
|
20
|
+
ports:
|
|
21
|
+
- "16380:6379"
|
|
22
|
+
healthcheck:
|
|
23
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
24
|
+
interval: 5s
|
|
25
|
+
timeout: 5s
|
|
26
|
+
retries: 10
|
|
27
|
+
networks:
|
|
28
|
+
- e2e
|
|
29
|
+
|
|
30
|
+
superset:
|
|
31
|
+
build:
|
|
32
|
+
context: .
|
|
33
|
+
dockerfile: Dockerfile
|
|
34
|
+
args:
|
|
35
|
+
SUPERSET_GIT_URL: https://github.com/apache/superset.git
|
|
36
|
+
SUPERSET_GIT_REF: "6.1.0"
|
|
37
|
+
environment:
|
|
38
|
+
- SUPERSET_CONFIG_PATH=/app/superset_config.py
|
|
39
|
+
- SUPERSET_GUEST_TOKEN_JWT_SECRET=e2e-test-secret-at-least-32-chars-long
|
|
40
|
+
- DATABASE_URL=postgresql+psycopg2://superset:superset@postgres:5432/superset
|
|
41
|
+
- REDIS_URL=redis://redis:6379/0
|
|
42
|
+
- SUPERSET_SECRET_KEY=e2e-test-secret-key-for-flask
|
|
43
|
+
- TALISMAN_ENABLED=false
|
|
44
|
+
ports:
|
|
45
|
+
- "8089:8088"
|
|
46
|
+
depends_on:
|
|
47
|
+
postgres:
|
|
48
|
+
condition: service_healthy
|
|
49
|
+
redis:
|
|
50
|
+
condition: service_healthy
|
|
51
|
+
healthcheck:
|
|
52
|
+
test: ["CMD-SHELL", "curl -sf http://localhost:8088/health || exit 1"]
|
|
53
|
+
interval: 10s
|
|
54
|
+
timeout: 10s
|
|
55
|
+
retries: 30
|
|
56
|
+
start_period: 60s
|
|
57
|
+
networks:
|
|
58
|
+
- e2e
|
|
59
|
+
|
|
60
|
+
networks:
|
|
61
|
+
e2e:
|
|
62
|
+
driver: bridge
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Superset Embed E2E Test Host</title>
|
|
6
|
+
<style>
|
|
7
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
8
|
+
body { font-family: -apple-system, sans-serif; height: 100vh; display: flex; flex-direction: column; }
|
|
9
|
+
.toolbar { padding: 12px 20px; background: #f0f0f0; border-bottom: 1px solid #ccc; display: flex; gap: 12px; align-items: center; }
|
|
10
|
+
.toolbar h1 { font-size: 16px; font-weight: 600; margin-right: auto; }
|
|
11
|
+
.toolbar button { padding: 8px 16px; border: 1px solid #ccc; border-radius: 4px; background: #fff; cursor: pointer; font-size: 14px; }
|
|
12
|
+
.toolbar button:hover { background: #e8e8e8; }
|
|
13
|
+
.toolbar button.active { background: #0066cc; color: #fff; border-color: #0066cc; }
|
|
14
|
+
#status { font-size: 12px; color: #666; padding: 4px 20px; background: #fafafa; }
|
|
15
|
+
.embed-container { flex: 1; position: relative; overflow: hidden; }
|
|
16
|
+
#supersetMount { width: 100%; height: 100%; }
|
|
17
|
+
#supersetMount iframe { width: 100%; height: 100%; border: 0; display: block; }
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div class="toolbar">
|
|
22
|
+
<h1>Superset Dashboard Embed E2E</h1>
|
|
23
|
+
<button id="btn-a" class="active">Dashboard A</button>
|
|
24
|
+
<button id="btn-b">Dashboard B</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div id="status">Initializing...</div>
|
|
27
|
+
<div class="embed-container">
|
|
28
|
+
<div id="supersetMount"></div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- The patched SDK is loaded from the Superset container -->
|
|
32
|
+
<script type="module">
|
|
33
|
+
// These values are injected by the test harness via config.js
|
|
34
|
+
const config = window.__E2E_CONFIG || {};
|
|
35
|
+
const supersetHost = config.supersetHost || 'http://localhost:8089';
|
|
36
|
+
const embeddedA = config.embeddedA || '';
|
|
37
|
+
const embeddedB = config.embeddedB || '';
|
|
38
|
+
const dashboardA = config.dashboardA || '';
|
|
39
|
+
const dashboardB = config.dashboardB || '';
|
|
40
|
+
|
|
41
|
+
const status = document.getElementById('status');
|
|
42
|
+
const mountPoint = document.getElementById('supersetMount');
|
|
43
|
+
|
|
44
|
+
// Import the patched SDK from the Superset container
|
|
45
|
+
// The SDK is built from patched source and served by Superset
|
|
46
|
+
let embeddedDashboard = null;
|
|
47
|
+
|
|
48
|
+
// Guest token fetch — calls Superset guest token API
|
|
49
|
+
async function fetchGuestToken(dashboardId) {
|
|
50
|
+
status.textContent = `Fetching guest token for ${dashboardId || 'initial'}...`;
|
|
51
|
+
const url = dashboardId
|
|
52
|
+
? `${supersetHost}/api/v1/security/guest_token/`
|
|
53
|
+
: `${supersetHost}/api/v1/security/guest_token/`;
|
|
54
|
+
|
|
55
|
+
// For e2e, we use the admin access token to create guest tokens
|
|
56
|
+
const loginResponse = await fetch(`${supersetHost}/api/v1/security/login`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'Content-Type': 'application/json' },
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
username: 'admin',
|
|
61
|
+
password: 'admin',
|
|
62
|
+
provider: 'db',
|
|
63
|
+
refresh: false,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
const loginData = await loginResponse.json();
|
|
67
|
+
const accessToken = loginData.access_token;
|
|
68
|
+
|
|
69
|
+
const targetDashboard = dashboardId || dashboardA;
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
74
|
+
'Content-Type': 'application/json',
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
user: { username: 'e2e-user', first_name: 'E2E', last_name: 'Test' },
|
|
78
|
+
resources: [{ type: 'dashboard', id: targetDashboard }],
|
|
79
|
+
rls: [],
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
const data = await response.json();
|
|
83
|
+
status.textContent = `Got guest token for ${targetDashboard}`;
|
|
84
|
+
return data.token;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function initEmbed() {
|
|
88
|
+
try {
|
|
89
|
+
status.textContent = 'Loading Superset embedded SDK...';
|
|
90
|
+
|
|
91
|
+
// Dynamically import the SDK from the Superset container
|
|
92
|
+
// The patched SDK is available at /superset-embedded-sdk/
|
|
93
|
+
const sdkModule = await import(`${supersetHost}/superset-embedded-sdk/dist/index.mjs`);
|
|
94
|
+
const { embedDashboard } = sdkModule;
|
|
95
|
+
|
|
96
|
+
status.textContent = 'Mounting embedded dashboard...';
|
|
97
|
+
|
|
98
|
+
embeddedDashboard = await embedDashboard({
|
|
99
|
+
id: embeddedA,
|
|
100
|
+
supersetDomain: supersetHost,
|
|
101
|
+
mountPoint: mountPoint,
|
|
102
|
+
fetchGuestToken: fetchGuestToken,
|
|
103
|
+
dashboardUiConfig: {
|
|
104
|
+
hideTitle: true,
|
|
105
|
+
hideTab: true,
|
|
106
|
+
hideChartControls: true,
|
|
107
|
+
filters: { visible: true, expanded: false },
|
|
108
|
+
urlParams: { standalone: '1' },
|
|
109
|
+
},
|
|
110
|
+
iframeTitle: 'Superset E2E Dashboard',
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
window.__E2E_READY = true;
|
|
114
|
+
window.__E2E_EMBEDDED = embeddedDashboard;
|
|
115
|
+
status.textContent = `READY — embedded: ${embeddedA}`;
|
|
116
|
+
|
|
117
|
+
// Capture the original iframe reference
|
|
118
|
+
const iframe = mountPoint.querySelector('iframe');
|
|
119
|
+
window.__ORIGINAL_IFRAME = iframe;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
status.textContent = `ERROR — ${err.message}`;
|
|
122
|
+
window.__E2E_ERROR = err.message;
|
|
123
|
+
console.error(err);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function switchDashboard(dashboardId) {
|
|
128
|
+
if (!embeddedDashboard) {
|
|
129
|
+
status.textContent = 'Embedded dashboard not ready';
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
status.textContent = `Switching to ${dashboardId}...`;
|
|
135
|
+
window.__E2E_SWITCH_RESULT = null;
|
|
136
|
+
|
|
137
|
+
const result = await embeddedDashboard.switchDashboard(dashboardId);
|
|
138
|
+
window.__E2E_SWITCH_RESULT = result;
|
|
139
|
+
status.textContent = `Switched to ${result.dashboardId}`;
|
|
140
|
+
} catch (err) {
|
|
141
|
+
status.textContent = `Switch error — ${err.message}`;
|
|
142
|
+
window.__E2E_ERROR = err.message;
|
|
143
|
+
console.error(err);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Button handlers
|
|
148
|
+
document.getElementById('btn-a').addEventListener('click', () => {
|
|
149
|
+
switchDashboard(dashboardA);
|
|
150
|
+
document.querySelectorAll('.toolbar button').forEach(b => b.classList.remove('active'));
|
|
151
|
+
document.getElementById('btn-a').classList.add('active');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
document.getElementById('btn-b').addEventListener('click', () => {
|
|
155
|
+
switchDashboard(dashboardB);
|
|
156
|
+
document.querySelectorAll('.toolbar button').forEach(b => b.classList.remove('active'));
|
|
157
|
+
document.getElementById('btn-b').classList.add('active');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Expose for Playwright
|
|
161
|
+
window.__E2E_SWITCH = switchDashboard;
|
|
162
|
+
window.__E2E_INIT = initEmbed;
|
|
163
|
+
|
|
164
|
+
// Start initialization
|
|
165
|
+
initEmbed();
|
|
166
|
+
</script>
|
|
167
|
+
</body>
|
|
168
|
+
</html>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Creates two dummy dashboards in Superset via the REST API.
|
|
4
|
+
# Also creates an embedded configuration for each dashboard and sets allowed domains.
|
|
5
|
+
#
|
|
6
|
+
# Usage: setup-dashboards.sh <superset_host>
|
|
7
|
+
# superset_host e.g. http://localhost:8089
|
|
8
|
+
#
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
SUPERSET_HOST=${1:?Usage: setup-dashboards.sh <superset_host>}
|
|
12
|
+
|
|
13
|
+
echo "Waiting for Superset to be ready..."
|
|
14
|
+
for i in $(seq 1 60); do
|
|
15
|
+
if curl -sf "${SUPERSET_HOST}/health" >/dev/null 2>&1; then
|
|
16
|
+
echo "Superset is ready."
|
|
17
|
+
break
|
|
18
|
+
fi
|
|
19
|
+
sleep 2
|
|
20
|
+
done
|
|
21
|
+
|
|
22
|
+
# Authenticate and get access token
|
|
23
|
+
echo "Authenticating..."
|
|
24
|
+
LOGIN_RESPONSE=$(curl -sf -X POST "${SUPERSET_HOST}/api/v1/security/login" \
|
|
25
|
+
-H "Content-Type: application/json" \
|
|
26
|
+
-d '{
|
|
27
|
+
"username": "admin",
|
|
28
|
+
"password": "admin",
|
|
29
|
+
"provider": "db",
|
|
30
|
+
"refresh": false
|
|
31
|
+
}')
|
|
32
|
+
|
|
33
|
+
ACCESS_TOKEN=$(echo "$LOGIN_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
|
|
34
|
+
echo "Got access token."
|
|
35
|
+
|
|
36
|
+
AUTH_HEADER="Authorization: Bearer ${ACCESS_TOKEN}"
|
|
37
|
+
|
|
38
|
+
# Create Dashboard A
|
|
39
|
+
echo "Creating Dashboard A..."
|
|
40
|
+
DASHBOARD_A_RESPONSE=$(curl -sf -X POST "${SUPERSET_HOST}/api/v1/dashboard/" \
|
|
41
|
+
-H "${AUTH_HEADER}" \
|
|
42
|
+
-H "Content-Type: application/json" \
|
|
43
|
+
-d '{
|
|
44
|
+
"dashboard_title": "E2E Test Dashboard A",
|
|
45
|
+
"slug": "e2e-dashboard-a",
|
|
46
|
+
"position_json": "{\"DASHBOARD_VERSION_KEY\": \"v2\"}",
|
|
47
|
+
"metadata": "{\"chart_configuration\": {}}"
|
|
48
|
+
}')
|
|
49
|
+
|
|
50
|
+
DASHBOARD_A_ID=$(echo "$DASHBOARD_A_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
|
51
|
+
echo "Created Dashboard A: $DASHBOARD_A_ID"
|
|
52
|
+
|
|
53
|
+
# Create Dashboard B
|
|
54
|
+
echo "Creating Dashboard B..."
|
|
55
|
+
DASHBOARD_B_RESPONSE=$(curl -sf -X POST "${SUPERSET_HOST}/api/v1/dashboard/" \
|
|
56
|
+
-H "${AUTH_HEADER}" \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{
|
|
59
|
+
"dashboard_title": "E2E Test Dashboard B",
|
|
60
|
+
"slug": "e2e-dashboard-b",
|
|
61
|
+
"position_json": "{\"DASHBOARD_VERSION_KEY\": \"v2\"}",
|
|
62
|
+
"metadata": "{\"chart_configuration\": {}}"
|
|
63
|
+
}')
|
|
64
|
+
|
|
65
|
+
DASHBOARD_B_ID=$(echo "$DASHBOARD_B_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
|
66
|
+
echo "Created Dashboard B: $DASHBOARD_B_ID"
|
|
67
|
+
|
|
68
|
+
# Create embedded configurations for both dashboards
|
|
69
|
+
echo "Creating embedded configuration for Dashboard A..."
|
|
70
|
+
EMBEDDED_A_RESPONSE=$(curl -sf -X POST "${SUPERSET_HOST}/api/v1/dashboard/embedded/" \
|
|
71
|
+
-H "${AUTH_HEADER}" \
|
|
72
|
+
-H "Content-Type: application/json" \
|
|
73
|
+
-d "{
|
|
74
|
+
\"resource_name\": \"Dashboard\",
|
|
75
|
+
\"resource_id\": ${DASHBOARD_A_ID},
|
|
76
|
+
\"allowed_domains\": [\"http://localhost:3002\", \"http://localhost:3001\"]
|
|
77
|
+
}")
|
|
78
|
+
|
|
79
|
+
EMBEDDED_A_UUID=$(echo "$EMBEDDED_A_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
|
|
80
|
+
echo "Created embedded config A: $EMBEDDED_A_UUID"
|
|
81
|
+
|
|
82
|
+
echo "Creating embedded configuration for Dashboard B..."
|
|
83
|
+
EMBEDDED_B_RESPONSE=$(curl -sf -X POST "${SUPERSET_HOST}/api/v1/dashboard/embedded/" \
|
|
84
|
+
-H "${AUTH_HEADER}" \
|
|
85
|
+
-H "Content-Type: application/json" \
|
|
86
|
+
-d "{
|
|
87
|
+
\"resource_name\": \"Dashboard\",
|
|
88
|
+
\"resource_id\": ${DASHBOARD_B_ID},
|
|
89
|
+
\"allowed_domains\": [\"http://localhost:3002\", \"http://localhost:3001\"]
|
|
90
|
+
}")
|
|
91
|
+
|
|
92
|
+
EMBEDDED_B_UUID=$(echo "$EMBEDDED_B_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['uuid'])")
|
|
93
|
+
echo "Created embedded config B: $EMBEDDED_B_UUID"
|
|
94
|
+
|
|
95
|
+
# Output the dashboard IDs for the test harness
|
|
96
|
+
cat > "$(dirname "$0")/dashboards.json" << EOF
|
|
97
|
+
{
|
|
98
|
+
"dashboardA": "${DASHBOARD_A_ID}",
|
|
99
|
+
"dashboardB": "${DASHBOARD_B_ID}",
|
|
100
|
+
"embeddedA": "${EMBEDDED_A_UUID}",
|
|
101
|
+
"embeddedB": "${EMBEDDED_B_UUID}"
|
|
102
|
+
}
|
|
103
|
+
EOF
|
|
104
|
+
|
|
105
|
+
echo "Dashboard setup complete."
|
|
106
|
+
cat "$(dirname "$0")/dashboards.json"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Superset configuration for e2e testing."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
# Database
|
|
6
|
+
SQLALCHEMY_DATABASE_URI = os.environ.get(
|
|
7
|
+
"DATABASE_URL", "postgresql+psycopg2://superset:superset@postgres:5432/superset"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
# Redis
|
|
11
|
+
REDIS_HOST = os.environ.get("REDIS_HOST", "redis")
|
|
12
|
+
REDIS_PORT = int(os.environ.get("REDIS_PORT", "6379"))
|
|
13
|
+
CELERY_REDIS_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/0"
|
|
14
|
+
CACHE_REDIS_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/1"
|
|
15
|
+
|
|
16
|
+
# Secret key
|
|
17
|
+
SECRET_KEY = os.environ.get("SUPERSET_SECRET_KEY", "e2e-test-secret-key-for-flask")
|
|
18
|
+
|
|
19
|
+
# Enable embedded dashboards
|
|
20
|
+
FEATURE_FLAGS = {
|
|
21
|
+
"EMBEDDED_SUPERSET": True,
|
|
22
|
+
"EMBEDDABLE_DASHBOARDS": True,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Guest token
|
|
26
|
+
GUEST_ROLE_NAME = "EmbeddedGuest"
|
|
27
|
+
GUEST_TOKEN_JWT_SECRET = os.environ.get("SUPERSET_GUEST_TOKEN_JWT_SECRET", "e2e-test-secret-at-least-32-chars-long")
|
|
28
|
+
GUEST_TOKEN_JWT_ALGO = "HS256"
|
|
29
|
+
GUEST_TOKEN_HEADER_NAME = "X-GuestToken"
|
|
30
|
+
|
|
31
|
+
# Disable Talisman for e2e (no HTTPS)
|
|
32
|
+
TALISMAN_ENABLED = False
|
|
33
|
+
|
|
34
|
+
# CORS
|
|
35
|
+
ENABLE_CORS = True
|
|
36
|
+
CORS_OPTIONS = {
|
|
37
|
+
"origins": ["*"],
|
|
38
|
+
"supports_credentials": False,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Allowed domains for embedded dashboards — allows localhost for e2e tests
|
|
42
|
+
# This is set per-dashboard via the API, but the default is permissive for e2e
|
|
43
|
+
HTTP_HEADERS = {}
|
|
44
|
+
|
|
45
|
+
# Session
|
|
46
|
+
SESSION_COOKIE_SECURE = False
|
|
47
|
+
|
|
48
|
+
# Logging
|
|
49
|
+
LOG_LEVEL = "INFO"
|