mirror of https://github.com/apache/superset.git
docs: add dynamic entity-relationship diagram to docs (#28130)
This commit is contained in:
parent
f155138659
commit
9db431b430
|
@ -23,7 +23,22 @@ jobs:
|
|||
# compatible/incompatible licenses addressed here: https://www.apache.org/legal/resolved.html
|
||||
# find SPDX identifiers here: https://spdx.org/licenses/
|
||||
deny-licenses: MS-LPL, BUSL-1.1, QPL-1.0, Sleepycat, SSPL-1.0, CPOL-1.02, AGPL-3.0, GPL-1.0+, BSD-4-Clause-UC, NPL-1.0, NPL-1.1, JSON
|
||||
# adding an exception for an ambigious license on store2, which has been resolved in the latest version. It's MIT: https://github.com/nbubna/store/blob/master/LICENSE-MIT
|
||||
# adding exception for all applitools modules (eyes-cypress and its dependencies), which has an explicit OSS license approved by ASF
|
||||
# license: https://applitools.com/legal/open-source-terms-of-use/
|
||||
allow-dependencies-licenses: 'pkg:npm/store2@2.14.2, pkg:npm/applitools/core, pkg:npm/applitools/core-base, pkg:npm/applitools/css-tree, pkg:npm/applitools/ec-client, pkg:npm/applitools/eg-socks5-proxy-server, pkg:npm/applitools/eyes, pkg:npm/applitools/eyes-cypress, pkg:npm/applitools/nml-client, pkg:npm/applitools/tunnel-client, pkg:npm/applitools/utils'
|
||||
allow-dependencies-licenses:
|
||||
# adding an exception for an ambigious license on store2, which has been resolved in
|
||||
# the latest version. It's MIT: https://github.com/nbubna/store/blob/master/LICENSE-MIT
|
||||
- 'pkg:npm/store2@2.14.2'
|
||||
# adding exception for all applitools modules (eyes-cypress and its dependencies),
|
||||
# which has an explicit OSS license approved by ASF
|
||||
# license: https://applitools.com/legal/open-source-terms-of-use/
|
||||
- 'pkg:npm/applitools/core'
|
||||
- 'pkg:npm/applitools/core-base'
|
||||
- 'pkg:npm/applitools/css-tree'
|
||||
- 'pkg:npm/applitools/ec-client'
|
||||
- 'pkg:npm/applitools/eg-socks5-proxy-server'
|
||||
- 'pkg:npm/applitools/eyes'
|
||||
- 'pkg:npm/applitools/eyes-cypress'
|
||||
- 'pkg:npm/applitools/nml-client'
|
||||
- 'pkg:npm/applitools/tunnel-client'
|
||||
- 'pkg:npm/applitools/utils'
|
||||
# Selecting BSD-3-Clause licensing terms for node-forge to ensure compatibility with Apache
|
||||
- 'pkg:npm/node-forge@1.3.1'
|
||||
|
|
|
@ -39,6 +39,13 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
- name: Compute Entity Relationship diagram (ERD)
|
||||
run: |
|
||||
python scripts/erd.py
|
||||
curl -L http://sourceforge.net/projects/plantuml/files/1.2023.7/plantuml.1.2023.7.jar/download > ~/plantuml.jar
|
||||
java -jar ~/plantuml.jar -v -tsvg -r -o "${{ github.workspace }}/docs/static/img/erd.svg" "${{ github.workspace }}/scripts/erd/erd.puml"
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install --check-cache
|
||||
|
|
|
@ -66,3 +66,7 @@ google-big-query.svg
|
|||
google-sheets.svg
|
||||
postgresql.svg
|
||||
snowflake.svg
|
||||
|
||||
# docs-related
|
||||
erd.puml
|
||||
erd.svg
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import InteractiveSVG from '../../src/components/InteractiveERDSVG';
|
||||
|
||||
# Entity-Relationship Diagram
|
||||
|
||||
Here is our interactive ERD:
|
||||
|
||||
<InteractiveSVG />
|
||||
|
||||
<br />
|
||||
|
||||
[Download the .svg](https://github.com/apache/superset/tree/master/docs/static/img/erd.svg)
|
|
@ -39,6 +39,7 @@
|
|||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-github-btn": "^1.4.0",
|
||||
"react-svg-pan-zoom": "^3.12.1",
|
||||
"stream": "^0.0.2",
|
||||
"swagger-ui-react": "^4.1.3",
|
||||
"url-loader": "^4.1.1"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { UncontrolledReactSVGPanZoom } from 'react-svg-pan-zoom';
|
||||
import ErdSvg from '../../static/img/erd.svg';
|
||||
|
||||
function InteractiveERDSVG() {
|
||||
return (
|
||||
<UncontrolledReactSVGPanZoom
|
||||
width="100%"
|
||||
height="800"
|
||||
background="#003153"
|
||||
tool="auto"
|
||||
>
|
||||
<svg>
|
||||
<ErdSvg />
|
||||
</svg>
|
||||
</UncontrolledReactSVGPanZoom>
|
||||
);
|
||||
}
|
||||
|
||||
export default InteractiveERDSVG;
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 177 KiB |
|
@ -1994,7 +1994,7 @@
|
|||
"@docusaurus/theme-search-algolia" "2.4.3"
|
||||
"@docusaurus/types" "2.4.3"
|
||||
|
||||
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
"@docusaurus/react-loadable@5.5.2":
|
||||
version "5.5.2"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz"
|
||||
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
|
||||
|
@ -8223,6 +8223,15 @@ prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
property-information@^5.0.0, property-information@^5.3.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz"
|
||||
|
@ -8841,7 +8850,7 @@ react-inspector@^5.1.1:
|
|||
is-dom "^1.0.0"
|
||||
prop-types "^15.0.0"
|
||||
|
||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
@ -8878,6 +8887,14 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.10.3"
|
||||
|
||||
"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
|
||||
version "5.5.2"
|
||||
resolved "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz"
|
||||
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-redux@^7.2.4:
|
||||
version "7.2.6"
|
||||
resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz"
|
||||
|
@ -8925,6 +8942,14 @@ react-router@5.3.4, react-router@^5.3.3:
|
|||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
|
||||
react-svg-pan-zoom@^3.12.1:
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/react-svg-pan-zoom/-/react-svg-pan-zoom-3.12.1.tgz#971de6163fbad0d2a98d3ad7eb09bd1941564376"
|
||||
integrity sha512-ug1LHCN5qed56C64xFypr/ClajuMFkig1OKvwJrIgGeSyHOjWM7XGgSgeP3IfHAkNw8QEc6a31ggZRpTijWYRw==
|
||||
dependencies:
|
||||
prop-types "^15.8.1"
|
||||
transformation-matrix "^2.14.0"
|
||||
|
||||
react-syntax-highlighter@^15.4.5:
|
||||
version "15.4.5"
|
||||
resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.5.tgz"
|
||||
|
@ -10069,6 +10094,11 @@ tr46@~0.0.3:
|
|||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
|
||||
|
||||
transformation-matrix@^2.14.0:
|
||||
version "2.16.1"
|
||||
resolved "https://registry.yarnpkg.com/transformation-matrix/-/transformation-matrix-2.16.1.tgz#4a2de06331b94ae953193d1b9a5ba002ec5f658a"
|
||||
integrity sha512-tdtC3wxVEuzU7X/ydL131Q3JU5cPMEn37oqVLITjRDSDsnSHVFzW2JiCLfZLIQEgWzZHdSy3J6bZzvKEN24jGA==
|
||||
|
||||
traverse@~0.6.6:
|
||||
version "0.6.6"
|
||||
resolved "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz"
|
||||
|
|
|
@ -0,0 +1,676 @@
|
|||
|
||||
@startuml erd
|
||||
|
||||
title Apache Superset ERD
|
||||
|
||||
!theme blueprint
|
||||
|
||||
' avoid problems with angled crows feet
|
||||
|
||||
skinparam linetype ortho
|
||||
skinparam classBorderColor #grey
|
||||
|
||||
skinparam classBorderColor<<new>> #white
|
||||
skinparam classBorderThickness<<new>> 1
|
||||
skinparam classLineStyle<<new>> Dashed
|
||||
skinparam ClassBackgroundColor<<new>> #204143
|
||||
|
||||
' Models
|
||||
rectangle "Data Assets" #black {
|
||||
entity "SqlMetric (sql_metrics)" as sql_metrics {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
metric_name: VARCHAR(255)
|
||||
verbose_name: VARCHAR(1024)
|
||||
metric_type: VARCHAR(32)
|
||||
description: TEXT
|
||||
d3format: VARCHAR(128)
|
||||
currency: VARCHAR(128)
|
||||
warning_text: TEXT
|
||||
table_id: INTEGER
|
||||
expression: TEXT
|
||||
extra: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "DatabaseUserOAuth2Tokens (database_user_oauth2_tokens)" as database_user_oauth2_tokens {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
user_id: INTEGER
|
||||
database_id: INTEGER
|
||||
access_token: BLOB
|
||||
access_token_expiration: DATETIME
|
||||
refresh_token: BLOB
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Table (sl_tables)" as sl_tables {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
database_id: INTEGER
|
||||
catalog: TEXT
|
||||
schema: TEXT
|
||||
name: TEXT
|
||||
is_managed_externally: BOOLEAN
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Database (dbs)" as dbs {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
verbose_name: VARCHAR(250)
|
||||
database_name: VARCHAR(250)
|
||||
sqlalchemy_uri: VARCHAR(1024)
|
||||
password: BLOB
|
||||
cache_timeout: INTEGER
|
||||
select_as_create_table_as: BOOLEAN
|
||||
expose_in_sqllab: BOOLEAN
|
||||
configuration_method: VARCHAR(255)
|
||||
allow_run_async: BOOLEAN
|
||||
allow_file_upload: BOOLEAN
|
||||
allow_ctas: BOOLEAN
|
||||
allow_cvas: BOOLEAN
|
||||
allow_dml: BOOLEAN
|
||||
force_ctas_schema: VARCHAR(250)
|
||||
extra: TEXT
|
||||
encrypted_extra: BLOB
|
||||
impersonate_user: BOOLEAN
|
||||
server_cert: BLOB
|
||||
is_managed_externally: BOOLEAN
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Dataset (sl_datasets)" as sl_datasets {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
database_id: INTEGER
|
||||
is_physical: BOOLEAN
|
||||
is_managed_externally: BOOLEAN
|
||||
name: TEXT
|
||||
expression: TEXT
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "SqlaTable (tables)" as tables {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
description: TEXT
|
||||
default_endpoint: TEXT
|
||||
is_featured: BOOLEAN
|
||||
filter_select_enabled: BOOLEAN
|
||||
offset: INTEGER
|
||||
cache_timeout: INTEGER
|
||||
params: VARCHAR(1000)
|
||||
perm: VARCHAR(1000)
|
||||
schema_perm: VARCHAR(1000)
|
||||
is_managed_externally: BOOLEAN
|
||||
external_url: TEXT
|
||||
table_name: VARCHAR(250)
|
||||
main_dttm_col: VARCHAR(250)
|
||||
database_id: INTEGER
|
||||
fetch_values_predicate: TEXT
|
||||
schema: VARCHAR(255)
|
||||
sql: TEXT
|
||||
is_sqllab_view: BOOLEAN
|
||||
template_params: TEXT
|
||||
extra: TEXT
|
||||
normalize_columns: BOOLEAN
|
||||
always_filter_main_dttm: BOOLEAN
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "TableColumn (table_columns)" as table_columns {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
column_name: VARCHAR(255)
|
||||
verbose_name: VARCHAR(1024)
|
||||
is_active: BOOLEAN
|
||||
type: TEXT
|
||||
advanced_data_type: VARCHAR(255)
|
||||
groupby: BOOLEAN
|
||||
filterable: BOOLEAN
|
||||
description: TEXT
|
||||
table_id: INTEGER
|
||||
is_dttm: BOOLEAN
|
||||
expression: TEXT
|
||||
python_date_format: VARCHAR(255)
|
||||
extra: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Column (sl_columns)" as sl_columns {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
is_additive: BOOLEAN
|
||||
is_aggregation: BOOLEAN
|
||||
is_filterable: BOOLEAN
|
||||
is_dimensional: BOOLEAN
|
||||
is_increase_desired: BOOLEAN
|
||||
is_managed_externally: BOOLEAN
|
||||
is_partition: BOOLEAN
|
||||
is_physical: BOOLEAN
|
||||
is_spatial: BOOLEAN
|
||||
is_temporal: BOOLEAN
|
||||
name: TEXT
|
||||
type: TEXT
|
||||
advanced_data_type: TEXT
|
||||
expression: TEXT
|
||||
unit: TEXT
|
||||
description: TEXT
|
||||
warning_text: TEXT
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "RowLevelSecurityFilter (row_level_security_filters)" as row_level_security_filters {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
name: VARCHAR(255)
|
||||
description: TEXT
|
||||
filter_type: VARCHAR(7)
|
||||
group_key: VARCHAR(255)
|
||||
clause: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
}
|
||||
rectangle "System" #black {
|
||||
entity "KeyValueEntry (key_value)" as key_value {
|
||||
uuid: BINARY(16)
|
||||
id: INTEGER
|
||||
resource: VARCHAR(32)
|
||||
value: BLOB
|
||||
created_on: DATETIME
|
||||
created_by_fk: INTEGER
|
||||
changed_on: DATETIME
|
||||
expires_on: DATETIME
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "SSHTunnel (ssh_tunnels)" as ssh_tunnels {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
database_id: INTEGER
|
||||
server_address: TEXT
|
||||
server_port: INTEGER
|
||||
username: BLOB
|
||||
password: BLOB
|
||||
private_key: BLOB
|
||||
private_key_password: BLOB
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "KeyValue (keyvalue)" as keyvalue {
|
||||
id: INTEGER
|
||||
value: TEXT
|
||||
}
|
||||
entity "CacheKey (cache_keys)" as cache_keys {
|
||||
id: INTEGER
|
||||
cache_key: VARCHAR(256)
|
||||
cache_timeout: INTEGER
|
||||
datasource_uid: VARCHAR(64)
|
||||
created_on: DATETIME
|
||||
}
|
||||
entity "Log (logs)" as logs {
|
||||
id: INTEGER
|
||||
action: VARCHAR(512)
|
||||
user_id: INTEGER
|
||||
dashboard_id: INTEGER
|
||||
slice_id: INTEGER
|
||||
json: TEXT
|
||||
dttm: DATETIME
|
||||
duration_ms: INTEGER
|
||||
referrer: VARCHAR(1024)
|
||||
}
|
||||
}
|
||||
rectangle "SQL Lab" #black {
|
||||
entity "SavedQuery (saved_query)" as saved_query {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
user_id: INTEGER
|
||||
db_id: INTEGER
|
||||
schema: VARCHAR(128)
|
||||
label: VARCHAR(256)
|
||||
description: TEXT
|
||||
sql: TEXT
|
||||
template_parameters: TEXT
|
||||
rows: INTEGER
|
||||
last_run: DATETIME
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "TableSchema (table_schema)" as table_schema {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
tab_state_id: INTEGER
|
||||
database_id: INTEGER
|
||||
schema: VARCHAR(256)
|
||||
table: VARCHAR(256)
|
||||
description: TEXT
|
||||
expanded: BOOLEAN
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Query (query)" as query {
|
||||
tracking_url: TEXT
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
client_id: VARCHAR(11)
|
||||
database_id: INTEGER
|
||||
tmp_table_name: VARCHAR(256)
|
||||
tmp_schema_name: VARCHAR(256)
|
||||
user_id: INTEGER
|
||||
status: VARCHAR(16)
|
||||
tab_name: VARCHAR(256)
|
||||
sql_editor_id: VARCHAR(256)
|
||||
schema: VARCHAR(256)
|
||||
sql: TEXT
|
||||
select_sql: TEXT
|
||||
executed_sql: TEXT
|
||||
limit: INTEGER
|
||||
limiting_factor: VARCHAR(18)
|
||||
select_as_cta: BOOLEAN
|
||||
select_as_cta_used: BOOLEAN
|
||||
ctas_method: VARCHAR(16)
|
||||
progress: INTEGER
|
||||
rows: INTEGER
|
||||
error_message: TEXT
|
||||
results_key: VARCHAR(64)
|
||||
start_time: NUMERIC(20, 6)
|
||||
start_running_time: NUMERIC(20, 6)
|
||||
end_time: NUMERIC(20, 6)
|
||||
end_result_backend_time: NUMERIC(20, 6)
|
||||
changed_on: DATETIME
|
||||
}
|
||||
entity "TabState (tab_state)" as tab_state {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
user_id: INTEGER
|
||||
label: VARCHAR(256)
|
||||
active: BOOLEAN
|
||||
database_id: INTEGER
|
||||
schema: VARCHAR(256)
|
||||
sql: TEXT
|
||||
query_limit: INTEGER
|
||||
latest_query_id: INTEGER
|
||||
autorun: BOOLEAN
|
||||
template_params: TEXT
|
||||
hide_left_bar: BOOLEAN
|
||||
saved_query_id: INTEGER
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
}
|
||||
rectangle "Core" #black {
|
||||
entity "FavStar (favstar)" as favstar {
|
||||
id: INTEGER
|
||||
user_id: INTEGER
|
||||
class_name: VARCHAR(50)
|
||||
obj_id: INTEGER
|
||||
dttm: DATETIME
|
||||
}
|
||||
entity "Dashboard (dashboards)" as dashboards {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
dashboard_title: VARCHAR(500)
|
||||
position_json: TEXT
|
||||
description: TEXT
|
||||
css: TEXT
|
||||
certified_by: TEXT
|
||||
certification_details: TEXT
|
||||
json_metadata: TEXT
|
||||
slug: VARCHAR(255)
|
||||
published: BOOLEAN
|
||||
is_managed_externally: BOOLEAN
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Annotation (annotation)" as annotation {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
start_dttm: DATETIME
|
||||
end_dttm: DATETIME
|
||||
layer_id: INTEGER
|
||||
short_descr: VARCHAR(500)
|
||||
long_descr: TEXT
|
||||
json_metadata: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "EmbeddedDashboard (embedded_dashboards)" as embedded_dashboards {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
uuid: BINARY(16)
|
||||
allow_domain_list: TEXT
|
||||
dashboard_id: INTEGER
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Slice (slices)" as slices {
|
||||
uuid: BINARY(16)
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
slice_name: VARCHAR(250)
|
||||
datasource_id: INTEGER
|
||||
datasource_type: VARCHAR(200)
|
||||
datasource_name: VARCHAR(2000)
|
||||
viz_type: VARCHAR(250)
|
||||
params: TEXT
|
||||
query_context: TEXT
|
||||
description: TEXT
|
||||
cache_timeout: INTEGER
|
||||
perm: VARCHAR(1000)
|
||||
schema_perm: VARCHAR(1000)
|
||||
last_saved_at: DATETIME
|
||||
last_saved_by_fk: INTEGER
|
||||
certified_by: TEXT
|
||||
certification_details: TEXT
|
||||
is_managed_externally: BOOLEAN
|
||||
external_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "TaggedObject (tagged_object)" as tagged_object {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
tag_id: INTEGER
|
||||
object_id: INTEGER
|
||||
object_type: VARCHAR(9)
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "AnnotationLayer (annotation_layer)" as annotation_layer {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
name: VARCHAR(250)
|
||||
descr: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "DynamicPlugin (dynamic_plugin)" as dynamic_plugin {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
name: TEXT
|
||||
key: TEXT
|
||||
bundle_url: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "Tag (tag)" as tag {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
name: VARCHAR(250)
|
||||
type: VARCHAR(12)
|
||||
description: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "CssTemplate (css_templates)" as css_templates {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
template_name: VARCHAR(250)
|
||||
css: TEXT
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "UserAttribute (user_attribute)" as user_attribute {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
user_id: INTEGER
|
||||
welcome_dashboard_id: INTEGER
|
||||
avatar_url: VARCHAR(100)
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
}
|
||||
rectangle "Inherited from Flask App Builder (FAB)" #black {
|
||||
entity "ViewMenu (ab_view_menu)" as ab_view_menu {
|
||||
id: INTEGER
|
||||
name: VARCHAR(250)
|
||||
}
|
||||
entity "Permission (ab_permission)" as ab_permission {
|
||||
id: INTEGER
|
||||
name: VARCHAR(100)
|
||||
}
|
||||
entity "User (ab_user)" as ab_user {
|
||||
id: INTEGER
|
||||
first_name: VARCHAR(64)
|
||||
last_name: VARCHAR(64)
|
||||
username: VARCHAR(64)
|
||||
password: VARCHAR(256)
|
||||
active: BOOLEAN
|
||||
email: VARCHAR(320)
|
||||
last_login: DATETIME
|
||||
login_count: INTEGER
|
||||
fail_login_count: INTEGER
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "RegisterUser (ab_register_user)" as ab_register_user {
|
||||
id: INTEGER
|
||||
first_name: VARCHAR(64)
|
||||
last_name: VARCHAR(64)
|
||||
username: VARCHAR(64)
|
||||
password: VARCHAR(256)
|
||||
email: VARCHAR(64)
|
||||
registration_date: DATETIME
|
||||
registration_hash: VARCHAR(256)
|
||||
}
|
||||
entity "PermissionView (ab_permission_view)" as ab_permission_view {
|
||||
id: INTEGER
|
||||
permission_id: INTEGER
|
||||
view_menu_id: INTEGER
|
||||
}
|
||||
entity "Role (ab_role)" as ab_role {
|
||||
id: INTEGER
|
||||
name: VARCHAR(64)
|
||||
}
|
||||
}
|
||||
rectangle "Alerts & Reports" #black {
|
||||
entity "ReportRecipients (report_recipient)" as report_recipient {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
id: INTEGER
|
||||
type: VARCHAR(50)
|
||||
recipient_config_json: TEXT
|
||||
report_schedule_id: INTEGER
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
entity "ReportExecutionLog (report_execution_log)" as report_execution_log {
|
||||
id: INTEGER
|
||||
uuid: BINARY(16)
|
||||
scheduled_dttm: DATETIME
|
||||
start_dttm: DATETIME
|
||||
end_dttm: DATETIME
|
||||
value: FLOAT
|
||||
value_row_json: TEXT
|
||||
state: VARCHAR(50)
|
||||
error_message: TEXT
|
||||
report_schedule_id: INTEGER
|
||||
}
|
||||
entity "ReportSchedule (report_schedule)" as report_schedule {
|
||||
created_on: DATETIME
|
||||
changed_on: DATETIME
|
||||
extra_json: TEXT
|
||||
id: INTEGER
|
||||
type: VARCHAR(50)
|
||||
name: VARCHAR(150)
|
||||
description: TEXT
|
||||
context_markdown: TEXT
|
||||
active: BOOLEAN
|
||||
crontab: VARCHAR(1000)
|
||||
creation_method: VARCHAR(255)
|
||||
timezone: VARCHAR(100)
|
||||
report_format: VARCHAR(50)
|
||||
sql: TEXT
|
||||
chart_id: INTEGER
|
||||
dashboard_id: INTEGER
|
||||
database_id: INTEGER
|
||||
last_eval_dttm: DATETIME
|
||||
last_state: VARCHAR(50)
|
||||
last_value: FLOAT
|
||||
last_value_row_json: TEXT
|
||||
validator_type: VARCHAR(100)
|
||||
validator_config_json: TEXT
|
||||
log_retention: INTEGER
|
||||
grace_period: INTEGER
|
||||
working_timeout: INTEGER
|
||||
force_screenshot: BOOLEAN
|
||||
custom_width: INTEGER
|
||||
custom_height: INTEGER
|
||||
created_by_fk: INTEGER
|
||||
changed_by_fk: INTEGER
|
||||
}
|
||||
}
|
||||
' Relationships
|
||||
|
||||
sql_metrics }|--|| tables
|
||||
sql_metrics }|--|| ab_user
|
||||
|
||||
database_user_oauth2_tokens }|--|| ab_user
|
||||
database_user_oauth2_tokens }|--|| dbs
|
||||
|
||||
sl_tables }|--|| dbs
|
||||
sl_tables }|--|{ sl_columns
|
||||
sl_tables }|--|| ab_user
|
||||
sl_tables }|--|{ sl_datasets
|
||||
|
||||
dbs }|--|| ab_user
|
||||
dbs ||--|{ tables
|
||||
dbs ||--|{ sl_datasets
|
||||
|
||||
sl_datasets }|--|{ sl_columns
|
||||
sl_datasets }|--|{ ab_user
|
||||
|
||||
tables ||--|{ table_columns
|
||||
tables }|--|{ row_level_security_filters
|
||||
|
||||
table_columns }|--|| ab_user
|
||||
|
||||
sl_columns }|--|| ab_user
|
||||
|
||||
row_level_security_filters }|--|| ab_user
|
||||
|
||||
key_value }|--|| ab_user
|
||||
|
||||
ssh_tunnels }|--|| dbs
|
||||
ssh_tunnels }|--|| ab_user
|
||||
|
||||
|
||||
|
||||
|
||||
saved_query }|--|| ab_user
|
||||
saved_query }|--|| dbs
|
||||
saved_query }|--|{ tag
|
||||
|
||||
table_schema }|--|| dbs
|
||||
table_schema }|--|| ab_user
|
||||
table_schema }|--|| tab_state
|
||||
|
||||
query }|--|| dbs
|
||||
query }|--|| ab_user
|
||||
|
||||
tab_state }|--|| dbs
|
||||
tab_state }|--|| query
|
||||
tab_state }|--|| saved_query
|
||||
tab_state }|--|| ab_user
|
||||
|
||||
|
||||
dashboards }|--|{ slices
|
||||
dashboards }|--|{ ab_user
|
||||
dashboards }|--|{ tag
|
||||
dashboards }|--|{ ab_role
|
||||
dashboards ||--|{ embedded_dashboards
|
||||
dashboards ||--|{ report_schedule
|
||||
|
||||
annotation }|--|| annotation_layer
|
||||
annotation }|--|| ab_user
|
||||
|
||||
embedded_dashboards }|--|| ab_user
|
||||
|
||||
slices }|--|| ab_user
|
||||
slices }|--|{ tag
|
||||
slices }|--|| tables
|
||||
slices ||--|{ report_schedule
|
||||
|
||||
tagged_object }|--|| tag
|
||||
tagged_object }|--|| ab_user
|
||||
|
||||
annotation_layer }|--|| ab_user
|
||||
|
||||
dynamic_plugin }|--|| ab_user
|
||||
|
||||
tag }|--|{ ab_user
|
||||
|
||||
css_templates }|--|| ab_user
|
||||
|
||||
user_attribute }|--|| dashboards
|
||||
|
||||
|
||||
|
||||
ab_user }|--|{ ab_role
|
||||
ab_user }|--|| ab_user
|
||||
ab_user ||--|{ logs
|
||||
ab_user ||--|{ user_attribute
|
||||
ab_user }|--|{ tables
|
||||
|
||||
|
||||
ab_permission_view }|--|| ab_permission
|
||||
ab_permission_view }|--|| ab_view_menu
|
||||
ab_permission_view }|--|{ ab_role
|
||||
|
||||
ab_role }|--|{ row_level_security_filters
|
||||
|
||||
report_recipient }|--|| report_schedule
|
||||
report_recipient }|--|| ab_user
|
||||
|
||||
report_execution_log }|--|| report_schedule
|
||||
|
||||
report_schedule }|--|| dbs
|
||||
report_schedule }|--|{ ab_user
|
||||
@enduml
|
|
@ -0,0 +1,211 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
This module contains utilities to auto-generate an
|
||||
Entity-Relationship Diagram (ERD) from SQLAlchemy
|
||||
and onto a plantuml file.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Optional
|
||||
|
||||
import click
|
||||
import jinja2
|
||||
from flask.cli import FlaskGroup, with_appcontext
|
||||
|
||||
from superset import app, db
|
||||
|
||||
GROUPINGS: dict[str, Iterable[str]] = {
|
||||
"Core": [
|
||||
"css_templates",
|
||||
"dynamic_plugin",
|
||||
"favstar",
|
||||
"dashboards",
|
||||
"slices",
|
||||
"user_attribute",
|
||||
"embedded_dashboards",
|
||||
"annotation",
|
||||
"annotation_layer",
|
||||
"tag",
|
||||
"tagged_object",
|
||||
],
|
||||
"System": ["ssh_tunnels", "keyvalue", "cache_keys", "key_value", "logs"],
|
||||
"Alerts & Reports": ["report_recipient", "report_execution_log", "report_schedule"],
|
||||
"Inherited from Flask App Builder (FAB)": [
|
||||
"ab_user",
|
||||
"ab_permission",
|
||||
"ab_permission_view",
|
||||
"ab_view_menu",
|
||||
"ab_role",
|
||||
"ab_register_user",
|
||||
],
|
||||
"SQL Lab": ["query", "saved_query", "tab_state", "table_schema"],
|
||||
"Data Assets": [
|
||||
"dbs",
|
||||
"table_columns",
|
||||
"sql_metrics",
|
||||
"tables",
|
||||
"row_level_security_filters",
|
||||
"sl_tables",
|
||||
"sl_datasets",
|
||||
"sl_columns",
|
||||
"database_user_oauth2_tokens",
|
||||
],
|
||||
}
|
||||
# Table name to group name mapping (reversing the above one for easy lookup)
|
||||
TABLE_TO_GROUP_MAP: dict[str, str] = {}
|
||||
for group, tables in GROUPINGS.items():
|
||||
for table in tables:
|
||||
TABLE_TO_GROUP_MAP[table] = group
|
||||
|
||||
|
||||
def sort_data_structure(data): # type: ignore
|
||||
sorted_json = json.dumps(data, sort_keys=True)
|
||||
sorted_data = json.loads(sorted_json)
|
||||
return sorted_data
|
||||
|
||||
|
||||
def introspect_sqla_model(mapper: Any, seen: set[str]) -> dict[str, Any]:
|
||||
"""
|
||||
Introspects a SQLAlchemy model and returns a data structure that
|
||||
can be pass to a jinja2 template for instance
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
mapper: SQLAlchemy model mapper
|
||||
seen: set of model identifiers to avoid duplicates
|
||||
|
||||
Returns:
|
||||
--------
|
||||
Dict[str, Any]: data structure for jinja2 template
|
||||
"""
|
||||
table_name = mapper.persist_selectable.name
|
||||
model_info: dict[str, Any] = {
|
||||
"class_name": mapper.class_.__name__,
|
||||
"table_name": table_name,
|
||||
"fields": [],
|
||||
"relationships": [],
|
||||
}
|
||||
# Collect fields (columns) and their types
|
||||
for column in mapper.columns:
|
||||
field_info: dict[str, str] = {
|
||||
"field_name": column.key,
|
||||
"type": str(column.type),
|
||||
}
|
||||
model_info["fields"].append(field_info)
|
||||
|
||||
# Collect relationships and identify types
|
||||
for attr, relationship in mapper.relationships.items():
|
||||
related_table = relationship.mapper.persist_selectable.name
|
||||
# Create a unique identifier for the relationship to avoid duplicates
|
||||
relationship_id = "-".join(sorted([table_name, related_table]))
|
||||
|
||||
if relationship_id not in seen:
|
||||
seen.add(relationship_id)
|
||||
squiggle = "||--|{"
|
||||
if relationship.direction.name == "MANYTOONE":
|
||||
squiggle = "}|--||"
|
||||
|
||||
relationship_info: dict[str, str] = {
|
||||
"relationship_name": attr,
|
||||
"related_model": relationship.mapper.class_.__name__,
|
||||
"type": relationship.direction.name,
|
||||
"related_table": related_table,
|
||||
}
|
||||
# Identify many-to-many by checking for secondary table
|
||||
if relationship.secondary is not None:
|
||||
squiggle = "}|--|{"
|
||||
relationship_info["type"] = "many-to-many"
|
||||
relationship_info["secondary_table"] = relationship.secondary.name
|
||||
|
||||
relationship_info["squiggle"] = squiggle
|
||||
model_info["relationships"].append(relationship_info)
|
||||
return sort_data_structure(model_info) # type: ignore
|
||||
|
||||
|
||||
def introspect_models() -> dict[str, list[dict[str, Any]]]:
|
||||
"""
|
||||
Introspects SQLAlchemy models and returns a data structure that
|
||||
can be pass to a jinja2 template for rendering an ERD.
|
||||
|
||||
Returns:
|
||||
--------
|
||||
Dict[str, List[Dict[str, Any]]]: data structure for jinja2 template
|
||||
"""
|
||||
data: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
||||
seen_models: set[str] = set()
|
||||
for model in db.Model.registry.mappers:
|
||||
group_name = (
|
||||
TABLE_TO_GROUP_MAP.get(model.mapper.persist_selectable.name)
|
||||
or "Uncategorized Models"
|
||||
)
|
||||
model_data = introspect_sqla_model(model, seen_models)
|
||||
data[group_name].append(model_data)
|
||||
return data
|
||||
|
||||
|
||||
def generate_erd(file_path: str) -> None:
|
||||
"""
|
||||
Generates a PlantUML ERD of the models/database
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
file_path: str
|
||||
File path to write the ERD to
|
||||
"""
|
||||
data = introspect_models()
|
||||
templates_path = os.path.dirname(__file__)
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(templates_path))
|
||||
|
||||
# Load the template
|
||||
template = env.get_template("erd.template.puml")
|
||||
rendered = template.render(data=data)
|
||||
with open(file_path, "w") as f:
|
||||
click.secho(f"Writing to {file_path}...", fg="green")
|
||||
f.write(rendered)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--output",
|
||||
"-o",
|
||||
type=click.Path(dir_okay=False, writable=True),
|
||||
help="File to write the ERD to",
|
||||
)
|
||||
def erd(output: Optional[str] = None) -> None:
|
||||
"""
|
||||
Generates a PlantUML ERD of the models/database
|
||||
|
||||
Parameters:
|
||||
-----------
|
||||
output: str, optional
|
||||
File to write the ERD to, defaults to erd.plantuml if not provided
|
||||
"""
|
||||
path = os.path.dirname(__file__)
|
||||
output = output or os.path.join(path, "erd.puml")
|
||||
|
||||
from superset.app import create_app
|
||||
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
generate_erd(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
erd()
|
|
@ -0,0 +1,57 @@
|
|||
{#
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
#}
|
||||
@startuml erd
|
||||
|
||||
title Apache Superset ERD
|
||||
|
||||
!theme blueprint
|
||||
|
||||
' avoid problems with angled crows feet
|
||||
|
||||
skinparam linetype ortho
|
||||
skinparam classBorderColor #grey
|
||||
|
||||
skinparam classBorderColor<<new>> #white
|
||||
skinparam classBorderThickness<<new>> 1
|
||||
skinparam classLineStyle<<new>> Dashed
|
||||
skinparam ClassBackgroundColor<<new>> #204143
|
||||
|
||||
' Models
|
||||
{% for group_name, models in data.items() -%}
|
||||
rectangle "{{ group_name }}" #black {
|
||||
{% for model in models -%}
|
||||
entity "{{ model.class_name }} ({{ model.table_name }})" as {{ model.table_name }} {
|
||||
{%- for field in model.fields %}
|
||||
{{ field.field_name }}: {{ field.type -}}
|
||||
{%- endfor %}
|
||||
}
|
||||
{% endfor -%}
|
||||
}
|
||||
{% endfor -%}
|
||||
|
||||
' Relationships
|
||||
{% for models in data.values() -%}
|
||||
{% for model in models -%}
|
||||
{%- for rel in model.relationships %}
|
||||
{{ model.table_name }} {{ rel.squiggle }} {{ rel.related_table }}
|
||||
{%- endfor %}
|
||||
{% endfor -%}
|
||||
{% endfor -%}
|
||||
|
||||
@enduml
|
|
@ -0,0 +1,57 @@
|
|||
{#
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
#}
|
||||
@startuml entity-relationship-diagram
|
||||
|
||||
title Apache Superset ERD
|
||||
|
||||
!theme blueprint
|
||||
|
||||
' avoid problems with angled crows feet
|
||||
|
||||
skinparam linetype ortho
|
||||
skinparam classBorderColor #grey
|
||||
|
||||
skinparam classBorderColor<<new>> #white
|
||||
skinparam classBorderThickness<<new>> 1
|
||||
skinparam classLineStyle<<new>> Dashed
|
||||
skinparam ClassBackgroundColor<<new>> #204143
|
||||
|
||||
' Models
|
||||
{% for group_name, models in data.items() -%}
|
||||
rectangle "{{ group_name }}" #black {
|
||||
{% for model in models -%}
|
||||
entity "{{ model.class_name }} ({{ model.table_name }})" as {{ model.table_name }} {
|
||||
{%- for field in model.fields %}
|
||||
{{ field.field_name }}: {{ field.type -}}
|
||||
{%- endfor %}
|
||||
}
|
||||
{% endfor -%}
|
||||
}
|
||||
{% endfor -%}
|
||||
|
||||
' Relationships
|
||||
{% for models in data.values() -%}
|
||||
{% for model in models -%}
|
||||
{%- for rel in model.relationships %}
|
||||
{{ model.table_name }} {{ rel.squiggle }} {{ rel.related_table }}
|
||||
{%- endfor %}
|
||||
{% endfor -%}
|
||||
{% endfor -%}
|
||||
|
||||
@enduml
|
Loading…
Reference in New Issue