Repository: fuelen/ecto_erd
Branch: main
Commit: da71179f04c3
Files: 63
Total size: 715.5 KB
Directory structure:
gitextract_ouem7f1z/
├── .formatter.exs
├── .gitignore
├── LICENSE.txt
├── README.md
├── examples/
│ ├── dbml/
│ │ ├── changelog.com/
│ │ │ ├── Clusters.dbml
│ │ │ └── Default.dbml
│ │ ├── hexpm/
│ │ │ ├── Contexts-as-clusters.dbml
│ │ │ ├── Default.dbml
│ │ │ └── Only-selected-cluster-Accounts-context.dbml
│ │ └── plausible-analytics/
│ │ ├── Contexts-as-clusters.dbml
│ │ └── Default.dbml
│ ├── dot/
│ │ ├── changelog.com/
│ │ │ ├── Clusters.dot
│ │ │ ├── Default.dot
│ │ │ └── No-fields.dot
│ │ ├── hexpm/
│ │ │ ├── Contexts-as-clusters-no-fields.dot
│ │ │ ├── Contexts-as-clusters.dot
│ │ │ ├── Default.dot
│ │ │ ├── No-fields.dot
│ │ │ ├── Only-embedded-schemas.dot
│ │ │ └── Only-selected-cluster-Accounts-context.dot
│ │ └── plausible-analytics/
│ │ ├── Contexts-as-clusters-no-fields.dot
│ │ ├── Contexts-as-clusters.dot
│ │ ├── Default.dot
│ │ └── No-fields.dot
│ ├── mermaid/
│ │ ├── changelog.com/
│ │ │ ├── Default.mmd
│ │ │ └── No-fields.mmd
│ │ ├── hexpm/
│ │ │ ├── Default.mmd
│ │ │ └── No-fields.mmd
│ │ └── plausible-analytics/
│ │ ├── Default.mmd
│ │ └── No-fields.mmd
│ ├── plantuml/
│ │ ├── changelog.com/
│ │ │ ├── Clusters.puml
│ │ │ └── Default.puml
│ │ ├── hexpm/
│ │ │ ├── Contexts-as-clusters-no-fields.puml
│ │ │ ├── Contexts-as-clusters.puml
│ │ │ ├── Default.puml
│ │ │ ├── Only-embedded-schemas.puml
│ │ │ └── Only-selected-cluster-Accounts-context.puml
│ │ └── plausible-analytics/
│ │ ├── Contexts-as-clusters-no-fields.puml
│ │ ├── Contexts-as-clusters.puml
│ │ └── Default.puml
│ └── quick_dbd/
│ ├── changelog.com/
│ │ └── Default.qdbd
│ ├── hexpm/
│ │ ├── Default.qdbd
│ │ └── Only-selected-cluster-Accounts-context.qdbd
│ └── plausible-analytics/
│ └── Default.qdbd
├── examples_generator.exs
├── lib/
│ ├── ecto/
│ │ └── erd/
│ │ ├── color.ex
│ │ ├── document/
│ │ │ ├── dbml.ex
│ │ │ ├── dot.ex
│ │ │ ├── mermaid.ex
│ │ │ ├── plantuml.ex
│ │ │ └── quick_dbd.ex
│ │ ├── document.ex
│ │ ├── edge.ex
│ │ ├── field.ex
│ │ ├── graph.ex
│ │ ├── html.ex
│ │ ├── node.ex
│ │ ├── render.ex
│ │ └── schema_modules.ex
│ └── mix/
│ └── tasks/
│ └── ecto.gen.erd.ex
├── mix.exs
└── test/
├── ecto/
│ └── erd_test.exs
└── test_helper.exs
================================================
FILE CONTENTS
================================================
================================================
FILE: .formatter.exs
================================================
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
================================================
FILE: .gitignore
================================================
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
ecto_erd-*.tar
# Temporary files, for example, from tests.
/tmp/
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
================================================
FILE: README.md
================================================
# Ecto.ERD
[](https://hex.pm/packages/ecto_erd)
A mix task for generating an ERD (Entity-Relationship Diagram) in various
formats for all Ecto schemas in your project.
Supported formats:
* [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) (default)
* [PlantUML](https://plantuml.com)
* [DBML](https://www.dbml.org/)
* [QuickDBD](https://www.quickdatabasediagrams.com)
* [Mermaid](https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram)

Definition of schemas
```elixir
defmodule Blog.Post do
use Ecto.Schema
schema "posts" do
field(:title, :string)
field(:text, :string)
timestamps()
belongs_to(:user, Blog.User)
has_many(:comments, Blog.Comment)
end
end
defmodule Blog.Comment do
use Ecto.Schema
schema "comments" do
field(:text, :string)
timestamps()
belongs_to(:post, Blog.Post)
belongs_to(:user, Blog.User)
end
end
defmodule Blog.User do
use Ecto.Schema
schema "users" do
field(:email, :string)
has_many(:posts, Blog.Post)
has_many(:comments, Blog.Comment)
end
end
```
## Installation
The package can be installed by adding `ecto_erd` to your list of dependencies
in `mix.exs`:
```elixir
def deps do
[
{:ecto_erd, "~> 0.6", only: :dev}
]
end
```
## Usage
Just run:
```sh
mix ecto.gen.erd
```
The command above produces a DOT file, which you can convert to an
image using the Graphviz utility:
```sh
dot -Tpng ecto_erd.dot -o erd.png
```
Configuration is possible via the `.ecto_erd.exs` file.
The docs can be found at [https://hexdocs.pm/ecto_erd](https://hexdocs.pm/ecto_erd).
Configuration examples and output for a few open-source projects can be
found in the PAGES section under EXAMPLES.
## Troubleshooting
Trying to run `ecto_erd` in an umbrella project? You might see this error:
```
$ mix ecto.gen.erd
** (RuntimeError) Unable to detect `:otp_app`, please specify it explicitly
```
The easiest solution is to run the command on one of the apps in the `apps/` directory. Another option is to create a configuration file and specify the `:otp_app`. See the [docs for details](https://hexdocs.pm/ecto_erd/Mix.Tasks.Ecto.Gen.Erd.html#module-configuration-file).
================================================
FILE: examples/dbml/changelog.com/Clusters.dbml
================================================
TableGroup EPISODE {
episodes
episode_guests
episode_hosts
episode_requests
episode_sponsors
episode_stats
episode_topics
}
TableGroup NEWS {
news_ads
news_issues
news_issue_ads
news_issue_items
news_items
news_item_comments
news_item_topics
news_queue
news_sources
news_sponsorships
}
TableGroup PERSON {
people
}
TableGroup PODCAST {
podcasts
podcast_hosts
podcast_topics
}
TableGroup POST {
posts
post_topics
}
TableGroup SPONSOR {
sponsors
sponsor_reps
}
Table feeds {
id integer [pk]
name varchar
slug varchar
description varchar
title_format varchar
plusplus boolean
autosub boolean
starts_at timestamp
cover varchar
podcast_ids array
person_ids array
owner_id integer
inserted_at timestamp
updated_at timestamp
}
Table subscriptions {
id integer [pk]
unsubscribed_at timestamp
context varchar
episode_id integer
item_id integer
person_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table topics {
id integer [pk]
name varchar
slug varchar
description varchar
website varchar
twitter_handle varchar
icon varchar
inserted_at timestamp
updated_at timestamp
}
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table oban_jobs {
id integer [pk]
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
}
Table episodes {
id integer [pk]
slug varchar
guid varchar
title varchar
subtitle varchar
type integer
featured boolean
highlight varchar
subhighlight varchar
summary varchar
notes varchar
doc_url varchar
socialize_url varchar
published boolean
published_at timestamp
recorded_at timestamp
recorded_live boolean
youtube_id varchar
cover varchar
audio_file varchar
audio_bytes integer
audio_duration integer
audio_chapters jsonb
plusplus_file varchar
plusplus_bytes integer
plusplus_duration integer
plusplus_chapters jsonb
download_count float
import_count float
reach_count integer
email_subject varchar
email_teaser varchar
email_content varchar
email_sends integer
email_opens integer
transcript array
podcast_id integer
request_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_guests {
id integer [pk]
position integer
thanks boolean
discount_code varchar
episode_id integer
person_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_hosts {
id integer [pk]
position integer
person_id integer
episode_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_requests {
id integer [pk]
status integer
hosts varchar
guests varchar
topics varchar
pitch varchar
pronunciation varchar
message varchar
podcast_id integer
submitter_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_sponsors {
id integer [pk]
position integer
title varchar
link_url varchar
description varchar
starts_at float
ends_at float
episode_id integer
sponsor_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_stats {
id integer [pk]
date date
episode_bytes integer
total_bytes integer
downloads float
uniques integer
demographics jsonb
episode_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_topics {
id integer [pk]
position integer
topic_id integer
episode_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_ads {
id integer [pk]
url varchar
headline varchar
story varchar
image varchar
active boolean
newsletter boolean
impression_count integer
click_count integer
sponsorship_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_issues {
id integer [pk]
slug varchar
note varchar
teaser varchar
published boolean
published_at timestamp
inserted_at timestamp
updated_at timestamp
}
Table news_issue_ads {
id integer [pk]
position integer
image boolean
ad_id integer
issue_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_issue_items {
id integer [pk]
position integer
image boolean
issue_id integer
item_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_items {
id integer [pk]
status integer
type integer
url varchar
headline varchar
story varchar
image varchar
object_id varchar
feed_only boolean
pinned boolean
published_at timestamp
refreshed_at timestamp
impression_count integer
click_count integer
message varchar
author_id integer
logger_id integer
submitter_id integer
source_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_item_comments {
id integer [pk]
content varchar
approved boolean
edited_at timestamp
deleted_at timestamp
item_id integer
author_id integer
parent_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_item_topics {
id integer [pk]
position integer
item_id integer
topic_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_queue {
id integer [pk]
position float
item_id integer
}
Table news_sources {
id integer [pk]
name varchar
slug varchar
website varchar
twitter_handle varchar
description varchar
feed varchar
regex varchar
publication boolean
icon varchar
inserted_at timestamp
updated_at timestamp
}
Table news_sponsorships {
id integer [pk]
name varchar
weeks array
impression_count integer
click_count integer
sponsor_id integer
inserted_at timestamp
updated_at timestamp
}
Table people {
id integer [pk]
name varchar
email varchar
handle varchar
github_handle varchar
linkedin_handle varchar
mastodon_handle varchar
twitter_handle varchar
slack_id varchar
website varchar
bio varchar
location varchar
auth_token varchar
auth_token_expires_at timestamp
joined_at timestamp
signed_in_at timestamp
approved boolean
avatar varchar
admin boolean
host boolean
editor boolean
public_profile boolean
settings jsonb
inserted_at timestamp
updated_at timestamp
}
Table podcasts {
id integer [pk]
name varchar
slug varchar
status integer
welcome varchar
description varchar
extended_description varchar
vanity_domain varchar
keywords varchar
mastodon_handle varchar
twitter_handle varchar
apple_url varchar
spotify_url varchar
riverside_url varchar
chartable_id varchar
schedule_note varchar
download_count float
reach_count integer
recorded_live boolean
partner boolean
position integer
subscribers jsonb
cover varchar
inserted_at timestamp
updated_at timestamp
}
Table podcast_hosts {
id integer [pk]
position integer
retired boolean
person_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table podcast_topics {
id integer [pk]
position integer
podcast_id integer
topic_id integer
inserted_at timestamp
updated_at timestamp
}
Table posts {
id integer [pk]
title varchar
subtitle varchar
slug varchar
guid varchar
canonical_url varchar
image varchar
tldr varchar
body varchar
published boolean
published_at timestamp
author_id integer
editor_id integer
inserted_at timestamp
updated_at timestamp
}
Table post_topics {
id integer [pk]
position integer
topic_id integer
post_id integer
inserted_at timestamp
updated_at timestamp
}
Table sponsors {
id integer [pk]
name varchar
description varchar
website varchar
github_handle varchar
twitter_handle varchar
avatar varchar
color_logo varchar
dark_logo varchar
light_logo varchar
inserted_at timestamp
updated_at timestamp
}
Table sponsor_reps {
id integer [pk]
sponsor_id integer
person_id integer
inserted_at timestamp
updated_at timestamp
}
Ref: episode_requests.id - episodes.request_id
Ref: episodes.id < episode_guests.episode_id
Ref: episodes.id < episode_hosts.episode_id
Ref: episodes.id < episode_sponsors.episode_id
Ref: episodes.id < episode_stats.episode_id
Ref: episodes.id < episode_topics.episode_id
Ref: episodes.id < subscriptions.episode_id
Ref: news_ads.id < news_issue_ads.ad_id
Ref: news_issues.id < news_issue_ads.issue_id
Ref: news_issues.id < news_issue_items.issue_id
Ref: news_item_comments.id < news_item_comments.parent_id
Ref: news_items.id < news_issue_items.item_id
Ref: news_items.id < news_item_comments.item_id
Ref: news_items.id < news_item_topics.item_id
Ref: news_items.id - news_queue.item_id
Ref: news_items.id < subscriptions.item_id
Ref: news_sources.id < news_items.source_id
Ref: news_sponsorships.id < news_ads.sponsorship_id
Ref: people.id < episode_guests.person_id
Ref: people.id < episode_hosts.person_id
Ref: people.id < episode_requests.submitter_id
Ref: people.id < feeds.owner_id
Ref: people.id < news_item_comments.author_id
Ref: people.id < news_items.author_id
Ref: people.id < news_items.logger_id
Ref: people.id < news_items.submitter_id
Ref: people.id < podcast_hosts.person_id
Ref: people.id < posts.author_id
Ref: people.id < posts.editor_id
Ref: people.id < sponsor_reps.person_id
Ref: people.id < subscriptions.person_id
Ref: podcasts.id < episode_requests.podcast_id
Ref: podcasts.id < episode_stats.podcast_id
Ref: podcasts.id < episodes.podcast_id
Ref: podcasts.id < podcast_hosts.podcast_id
Ref: podcasts.id < podcast_topics.podcast_id
Ref: podcasts.id < subscriptions.podcast_id
Ref: posts.id < post_topics.post_id
Ref: sponsors.id < episode_sponsors.sponsor_id
Ref: sponsors.id < news_sponsorships.sponsor_id
Ref: sponsors.id < sponsor_reps.sponsor_id
Ref: topics.id < episode_topics.topic_id
Ref: topics.id < news_item_topics.topic_id
Ref: topics.id < podcast_topics.topic_id
Ref: topics.id < post_topics.topic_id
================================================
FILE: examples/dbml/changelog.com/Default.dbml
================================================
Table episodes {
id integer [pk]
slug varchar
guid varchar
title varchar
subtitle varchar
type integer
featured boolean
highlight varchar
subhighlight varchar
summary varchar
notes varchar
doc_url varchar
socialize_url varchar
published boolean
published_at timestamp
recorded_at timestamp
recorded_live boolean
youtube_id varchar
cover varchar
audio_file varchar
audio_bytes integer
audio_duration integer
audio_chapters jsonb
plusplus_file varchar
plusplus_bytes integer
plusplus_duration integer
plusplus_chapters jsonb
download_count float
import_count float
reach_count integer
email_subject varchar
email_teaser varchar
email_content varchar
email_sends integer
email_opens integer
transcript array
podcast_id integer
request_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_guests {
id integer [pk]
position integer
thanks boolean
discount_code varchar
episode_id integer
person_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_hosts {
id integer [pk]
position integer
person_id integer
episode_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_requests {
id integer [pk]
status integer
hosts varchar
guests varchar
topics varchar
pitch varchar
pronunciation varchar
message varchar
podcast_id integer
submitter_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_sponsors {
id integer [pk]
position integer
title varchar
link_url varchar
description varchar
starts_at float
ends_at float
episode_id integer
sponsor_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_stats {
id integer [pk]
date date
episode_bytes integer
total_bytes integer
downloads float
uniques integer
demographics jsonb
episode_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table episode_topics {
id integer [pk]
position integer
topic_id integer
episode_id integer
inserted_at timestamp
updated_at timestamp
}
Table feeds {
id integer [pk]
name varchar
slug varchar
description varchar
title_format varchar
plusplus boolean
autosub boolean
starts_at timestamp
cover varchar
podcast_ids array
person_ids array
owner_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_ads {
id integer [pk]
url varchar
headline varchar
story varchar
image varchar
active boolean
newsletter boolean
impression_count integer
click_count integer
sponsorship_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_issues {
id integer [pk]
slug varchar
note varchar
teaser varchar
published boolean
published_at timestamp
inserted_at timestamp
updated_at timestamp
}
Table news_issue_ads {
id integer [pk]
position integer
image boolean
ad_id integer
issue_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_issue_items {
id integer [pk]
position integer
image boolean
issue_id integer
item_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_items {
id integer [pk]
status integer
type integer
url varchar
headline varchar
story varchar
image varchar
object_id varchar
feed_only boolean
pinned boolean
published_at timestamp
refreshed_at timestamp
impression_count integer
click_count integer
message varchar
author_id integer
logger_id integer
submitter_id integer
source_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_item_comments {
id integer [pk]
content varchar
approved boolean
edited_at timestamp
deleted_at timestamp
item_id integer
author_id integer
parent_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_item_topics {
id integer [pk]
position integer
item_id integer
topic_id integer
inserted_at timestamp
updated_at timestamp
}
Table news_queue {
id integer [pk]
position float
item_id integer
}
Table news_sources {
id integer [pk]
name varchar
slug varchar
website varchar
twitter_handle varchar
description varchar
feed varchar
regex varchar
publication boolean
icon varchar
inserted_at timestamp
updated_at timestamp
}
Table news_sponsorships {
id integer [pk]
name varchar
weeks array
impression_count integer
click_count integer
sponsor_id integer
inserted_at timestamp
updated_at timestamp
}
Table people {
id integer [pk]
name varchar
email varchar
handle varchar
github_handle varchar
linkedin_handle varchar
mastodon_handle varchar
twitter_handle varchar
slack_id varchar
website varchar
bio varchar
location varchar
auth_token varchar
auth_token_expires_at timestamp
joined_at timestamp
signed_in_at timestamp
approved boolean
avatar varchar
admin boolean
host boolean
editor boolean
public_profile boolean
settings jsonb
inserted_at timestamp
updated_at timestamp
}
Table podcasts {
id integer [pk]
name varchar
slug varchar
status integer
welcome varchar
description varchar
extended_description varchar
vanity_domain varchar
keywords varchar
mastodon_handle varchar
twitter_handle varchar
apple_url varchar
spotify_url varchar
riverside_url varchar
chartable_id varchar
schedule_note varchar
download_count float
reach_count integer
recorded_live boolean
partner boolean
position integer
subscribers jsonb
cover varchar
inserted_at timestamp
updated_at timestamp
}
Table podcast_hosts {
id integer [pk]
position integer
retired boolean
person_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table podcast_topics {
id integer [pk]
position integer
podcast_id integer
topic_id integer
inserted_at timestamp
updated_at timestamp
}
Table posts {
id integer [pk]
title varchar
subtitle varchar
slug varchar
guid varchar
canonical_url varchar
image varchar
tldr varchar
body varchar
published boolean
published_at timestamp
author_id integer
editor_id integer
inserted_at timestamp
updated_at timestamp
}
Table post_topics {
id integer [pk]
position integer
topic_id integer
post_id integer
inserted_at timestamp
updated_at timestamp
}
Table sponsors {
id integer [pk]
name varchar
description varchar
website varchar
github_handle varchar
twitter_handle varchar
avatar varchar
color_logo varchar
dark_logo varchar
light_logo varchar
inserted_at timestamp
updated_at timestamp
}
Table sponsor_reps {
id integer [pk]
sponsor_id integer
person_id integer
inserted_at timestamp
updated_at timestamp
}
Table subscriptions {
id integer [pk]
unsubscribed_at timestamp
context varchar
episode_id integer
item_id integer
person_id integer
podcast_id integer
inserted_at timestamp
updated_at timestamp
}
Table topics {
id integer [pk]
name varchar
slug varchar
description varchar
website varchar
twitter_handle varchar
icon varchar
inserted_at timestamp
updated_at timestamp
}
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table oban_jobs {
id integer [pk]
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
}
Ref: episode_requests.id - episodes.request_id
Ref: episodes.id < episode_guests.episode_id
Ref: episodes.id < episode_hosts.episode_id
Ref: episodes.id < episode_sponsors.episode_id
Ref: episodes.id < episode_stats.episode_id
Ref: episodes.id < episode_topics.episode_id
Ref: episodes.id < subscriptions.episode_id
Ref: news_ads.id < news_issue_ads.ad_id
Ref: news_issues.id < news_issue_ads.issue_id
Ref: news_issues.id < news_issue_items.issue_id
Ref: news_item_comments.id < news_item_comments.parent_id
Ref: news_items.id < news_issue_items.item_id
Ref: news_items.id < news_item_comments.item_id
Ref: news_items.id < news_item_topics.item_id
Ref: news_items.id - news_queue.item_id
Ref: news_items.id < subscriptions.item_id
Ref: news_sources.id < news_items.source_id
Ref: news_sponsorships.id < news_ads.sponsorship_id
Ref: people.id < episode_guests.person_id
Ref: people.id < episode_hosts.person_id
Ref: people.id < episode_requests.submitter_id
Ref: people.id < feeds.owner_id
Ref: people.id < news_item_comments.author_id
Ref: people.id < news_items.author_id
Ref: people.id < news_items.logger_id
Ref: people.id < news_items.submitter_id
Ref: people.id < podcast_hosts.person_id
Ref: people.id < posts.author_id
Ref: people.id < posts.editor_id
Ref: people.id < sponsor_reps.person_id
Ref: people.id < subscriptions.person_id
Ref: podcasts.id < episode_requests.podcast_id
Ref: podcasts.id < episode_stats.podcast_id
Ref: podcasts.id < episodes.podcast_id
Ref: podcasts.id < podcast_hosts.podcast_id
Ref: podcasts.id < podcast_topics.podcast_id
Ref: podcasts.id < subscriptions.podcast_id
Ref: posts.id < post_topics.post_id
Ref: sponsors.id < episode_sponsors.sponsor_id
Ref: sponsors.id < news_sponsorships.sponsor_id
Ref: sponsors.id < sponsor_reps.sponsor_id
Ref: topics.id < episode_topics.topic_id
Ref: topics.id < news_item_topics.topic_id
Ref: topics.id < podcast_topics.topic_id
Ref: topics.id < post_topics.topic_id
================================================
FILE: examples/dbml/hexpm/Contexts-as-clusters.dbml
================================================
TableGroup "Ecto.Migration" {
schema_migrations
}
TableGroup "Hexpm.Accounts" {
audit_logs
emails
keys
organizations
organization_users
password_resets
sessions
users
}
TableGroup "Hexpm.BlockAddress" {
blocked_addresses
}
TableGroup "Hexpm.Repository" {
downloads
installs
packages
package_dependants
package_downloads
package_owners
package_reports
package_report_comments
package_report_releases
releases
release_downloads
repositories
requirements
}
TableGroup "Hexpm.ShortURLs" {
short_urls
}
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table audit_logs {
id integer [pk]
user_agent varchar
remote_ip varchar
action varchar
params jsonb
user_id integer
organization_id integer
key_id integer
inserted_at timestamp
}
Table emails {
id integer [pk]
email varchar
verified boolean
primary boolean
public boolean
gravatar boolean
verification_key varchar
verification_expiry timestamp
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table keys {
id integer [pk]
name varchar
secret_first varchar
secret_second varchar
public boolean
revoke_at timestamp
inserted_at timestamp
updated_at timestamp
last_use jsonb
user_id integer
organization_id integer
permissions jsonb
}
Table organizations {
id integer [pk]
name varchar
billing_active boolean
billing_override boolean
trial_end timestamp
inserted_at timestamp
updated_at timestamp
}
Table organization_users {
id integer [pk]
role varchar
organization_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table password_resets {
id integer [pk]
key varchar
primary_email varchar
user_id integer
inserted_at timestamp
}
Table sessions {
id integer [pk]
token bytea
data jsonb
inserted_at timestamp
updated_at timestamp
}
Table users {
id integer [pk]
username varchar
full_name varchar
password varchar
service boolean
deactivated_at timestamp
role varchar
inserted_at timestamp
updated_at timestamp
handles jsonb
tfa jsonb
organization_id integer
}
Table blocked_addresses {
id integer [pk]
ip varchar
comment varchar
}
Table downloads {
id integer [pk]
package_id integer
release_id integer
downloads integer
day date
}
Table installs {
id integer [pk]
hex varchar
elixirs array
}
Table packages {
id integer [pk]
name varchar
docs_updated_at timestamp
inserted_at timestamp
updated_at timestamp
repository_id integer
meta jsonb
}
Table package_dependants {
id integer [pk]
package_id integer
name varchar
repo varchar
}
Table package_downloads {
package_id integer
view varchar
downloads integer
}
Table package_owners {
id integer [pk]
level varchar
package_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table package_reports {
id integer [pk]
state varchar
description varchar
author_id integer
package_id integer
inserted_at timestamp
updated_at timestamp
}
Table package_report_comments {
id integer [pk]
text varchar
inserted_at timestamp
updated_at timestamp
package_report_id integer
author_id integer
}
Table package_report_releases {
id integer [pk]
release_id integer
package_report_id integer
inserted_at timestamp
updated_at timestamp
}
Table releases {
id integer [pk]
version varchar
inner_checksum bytea
outer_checksum bytea
has_docs boolean
inserted_at timestamp
updated_at timestamp
package_id integer
publisher_id integer
meta jsonb
retirement jsonb
}
Table release_downloads {
package_id integer
release_id integer
downloads integer
}
Table repositories {
id integer [pk]
name varchar
inserted_at timestamp
updated_at timestamp
organization_id integer
}
Table requirements {
id integer [pk]
app varchar
requirement varchar
optional boolean
release_id integer
dependency_id integer
}
Table short_urls {
id integer [pk]
url varchar
short_code varchar
inserted_at timestamp
}
Ref: keys.id < audit_logs.key_id
Ref: organizations.id < audit_logs.organization_id
Ref: organizations.id < keys.organization_id
Ref: organizations.id < organization_users.organization_id
Ref: organizations.id - repositories.organization_id
Ref: organizations.id - users.organization_id
Ref: package_reports.id < package_report_comments.package_report_id
Ref: package_reports.id < package_report_releases.package_report_id
Ref: packages.id < downloads.package_id
Ref: packages.id < package_dependants.package_id
Ref: packages.id < package_downloads.package_id
Ref: packages.id < package_owners.package_id
Ref: packages.id < package_reports.package_id
Ref: packages.id < release_downloads.package_id
Ref: packages.id < releases.package_id
Ref: packages.id < requirements.dependency_id
Ref: releases.id < downloads.release_id
Ref: releases.id < package_report_releases.release_id
Ref: releases.id - release_downloads.release_id
Ref: releases.id < requirements.release_id
Ref: repositories.id < packages.repository_id
Ref: users.id < audit_logs.user_id
Ref: users.id < emails.user_id
Ref: users.id < keys.user_id
Ref: users.id < organization_users.user_id
Ref: users.id < package_owners.user_id
Ref: users.id < package_report_comments.author_id
Ref: users.id < package_reports.author_id
Ref: users.id < password_resets.user_id
Ref: users.id < releases.publisher_id
================================================
FILE: examples/dbml/hexpm/Default.dbml
================================================
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table audit_logs {
id integer [pk]
user_agent varchar
remote_ip varchar
action varchar
params jsonb
user_id integer
organization_id integer
key_id integer
inserted_at timestamp
}
Table emails {
id integer [pk]
email varchar
verified boolean
primary boolean
public boolean
gravatar boolean
verification_key varchar
verification_expiry timestamp
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table keys {
id integer [pk]
name varchar
secret_first varchar
secret_second varchar
public boolean
revoke_at timestamp
inserted_at timestamp
updated_at timestamp
last_use jsonb
user_id integer
organization_id integer
permissions jsonb
}
Table organizations {
id integer [pk]
name varchar
billing_active boolean
billing_override boolean
trial_end timestamp
inserted_at timestamp
updated_at timestamp
}
Table organization_users {
id integer [pk]
role varchar
organization_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table password_resets {
id integer [pk]
key varchar
primary_email varchar
user_id integer
inserted_at timestamp
}
Table sessions {
id integer [pk]
token bytea
data jsonb
inserted_at timestamp
updated_at timestamp
}
Table users {
id integer [pk]
username varchar
full_name varchar
password varchar
service boolean
deactivated_at timestamp
role varchar
inserted_at timestamp
updated_at timestamp
handles jsonb
tfa jsonb
organization_id integer
}
Table blocked_addresses {
id integer [pk]
ip varchar
comment varchar
}
Table downloads {
id integer [pk]
package_id integer
release_id integer
downloads integer
day date
}
Table installs {
id integer [pk]
hex varchar
elixirs array
}
Table packages {
id integer [pk]
name varchar
docs_updated_at timestamp
inserted_at timestamp
updated_at timestamp
repository_id integer
meta jsonb
}
Table package_dependants {
id integer [pk]
package_id integer
name varchar
repo varchar
}
Table package_downloads {
package_id integer
view varchar
downloads integer
}
Table package_owners {
id integer [pk]
level varchar
package_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table package_reports {
id integer [pk]
state varchar
description varchar
author_id integer
package_id integer
inserted_at timestamp
updated_at timestamp
}
Table package_report_comments {
id integer [pk]
text varchar
inserted_at timestamp
updated_at timestamp
package_report_id integer
author_id integer
}
Table package_report_releases {
id integer [pk]
release_id integer
package_report_id integer
inserted_at timestamp
updated_at timestamp
}
Table releases {
id integer [pk]
version varchar
inner_checksum bytea
outer_checksum bytea
has_docs boolean
inserted_at timestamp
updated_at timestamp
package_id integer
publisher_id integer
meta jsonb
retirement jsonb
}
Table release_downloads {
package_id integer
release_id integer
downloads integer
}
Table repositories {
id integer [pk]
name varchar
inserted_at timestamp
updated_at timestamp
organization_id integer
}
Table requirements {
id integer [pk]
app varchar
requirement varchar
optional boolean
release_id integer
dependency_id integer
}
Table short_urls {
id integer [pk]
url varchar
short_code varchar
inserted_at timestamp
}
Ref: keys.id < audit_logs.key_id
Ref: organizations.id < audit_logs.organization_id
Ref: organizations.id < keys.organization_id
Ref: organizations.id < organization_users.organization_id
Ref: organizations.id - repositories.organization_id
Ref: organizations.id - users.organization_id
Ref: package_reports.id < package_report_comments.package_report_id
Ref: package_reports.id < package_report_releases.package_report_id
Ref: packages.id < downloads.package_id
Ref: packages.id < package_dependants.package_id
Ref: packages.id < package_downloads.package_id
Ref: packages.id < package_owners.package_id
Ref: packages.id < package_reports.package_id
Ref: packages.id < release_downloads.package_id
Ref: packages.id < releases.package_id
Ref: packages.id < requirements.dependency_id
Ref: releases.id < downloads.release_id
Ref: releases.id < package_report_releases.release_id
Ref: releases.id - release_downloads.release_id
Ref: releases.id < requirements.release_id
Ref: repositories.id < packages.repository_id
Ref: users.id < audit_logs.user_id
Ref: users.id < emails.user_id
Ref: users.id < keys.user_id
Ref: users.id < organization_users.user_id
Ref: users.id < package_owners.user_id
Ref: users.id < package_report_comments.author_id
Ref: users.id < package_reports.author_id
Ref: users.id < password_resets.user_id
Ref: users.id < releases.publisher_id
================================================
FILE: examples/dbml/hexpm/Only-selected-cluster-Accounts-context.dbml
================================================
TableGroup "Hexpm.Accounts" {
audit_logs
emails
keys
organizations
organization_users
password_resets
sessions
users
}
Table audit_logs {
id integer [pk]
user_agent varchar
remote_ip varchar
action varchar
params jsonb
user_id integer
organization_id integer
key_id integer
inserted_at timestamp
}
Table emails {
id integer [pk]
email varchar
verified boolean
primary boolean
public boolean
gravatar boolean
verification_key varchar
verification_expiry timestamp
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table keys {
id integer [pk]
name varchar
secret_first varchar
secret_second varchar
public boolean
revoke_at timestamp
inserted_at timestamp
updated_at timestamp
last_use jsonb
user_id integer
organization_id integer
permissions jsonb
}
Table organizations {
id integer [pk]
name varchar
billing_active boolean
billing_override boolean
trial_end timestamp
inserted_at timestamp
updated_at timestamp
}
Table organization_users {
id integer [pk]
role varchar
organization_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table password_resets {
id integer [pk]
key varchar
primary_email varchar
user_id integer
inserted_at timestamp
}
Table sessions {
id integer [pk]
token bytea
data jsonb
inserted_at timestamp
updated_at timestamp
}
Table users {
id integer [pk]
username varchar
full_name varchar
password varchar
service boolean
deactivated_at timestamp
role varchar
inserted_at timestamp
updated_at timestamp
handles jsonb
tfa jsonb
organization_id integer
}
Ref: keys.id < audit_logs.key_id
Ref: organizations.id < audit_logs.organization_id
Ref: organizations.id < keys.organization_id
Ref: organizations.id < organization_users.organization_id
Ref: organizations.id - users.organization_id
Ref: users.id < audit_logs.user_id
Ref: users.id < emails.user_id
Ref: users.id < keys.user_id
Ref: users.id < organization_users.user_id
Ref: users.id < password_resets.user_id
================================================
FILE: examples/dbml/plausible-analytics/Contexts-as-clusters.dbml
================================================
TableGroup "Ecto.Migration" {
schema_migrations
}
TableGroup "FunWithFlags.Store" {
fun_with_flags_toggles
}
TableGroup Oban {
oban_jobs
}
TableGroup Plausible {
events_v2
sessions_v2
funnels
goals
sites
}
TableGroup "Plausible.Auth" {
api_keys
email_activation_codes
invitations
totp_recovery_codes
users
}
TableGroup "Plausible.Billing" {
enterprise_plans
subscriptions
}
TableGroup "Plausible.DataMigration" {
domains_lookup
}
TableGroup "Plausible.Funnel" {
funnel_steps
}
TableGroup "Plausible.Imported" {
imported_browsers
imported_devices
imported_entry_pages
imported_exit_pages
imported_locations
imported_operating_systems
imported_pages
site_imports
imported_sources
imported_visitors
}
TableGroup "Plausible.Ingestion" {
ingest_counters
}
TableGroup "Plausible.Plugins" {
plugins_api_tokens
}
TableGroup "Plausible.Shield" {
shield_rules_country
shield_rules_hostname
shield_rules_ip
shield_rules_page
}
TableGroup "Plausible.Site" {
google_auth
site_memberships
monthly_reports
shared_links
spike_notifications
site_user_preferences
weekly_reports
}
Enum billing_interval {
yearly
monthly
}
Enum currency {
XBD
BYN
HKD
XOF
SOS
ARS
EGP
XDR
GMD
MAD
XAG
XAU
COU
UYW
LKR
SAR
BBD
XCD
ZMW
ZWL
CZK
JPY
SEK
PLN
KYD
THB
QAR
SLL
RON
CUC
MOP
CHW
KGS
ALL
CLP
XXX
IDR
BZD
PYG
LAK
OMR
HRK
CHF
BTN
MRU
GEL
BOV
AFN
RSD
XTS
UYU
BHD
HNL
GBP
WST
COP
MKD
ZAR
SYP
SZL
HTG
SVC
NPR
MXV
MMK
PKR
GTQ
AWG
SGD
TWD
AOA
TOP
XBB
KRW
XBA
TRY
XPT
SBD
MUR
NIO
CNY
BWP
NOK
LSL
IRR
BOB
BRL
SDG
BIF
BDT
UYI
NGN
LBP
GYD
RWF
ILS
PGK
TTD
SSP
MWK
ETB
INR
AUD
XPD
CVE
TMT
YER
BAM
XSU
MVR
SCR
JOD
CHE
CUP
PAB
MGA
VES
JMD
VUV
MNT
NZD
XBC
KES
GNF
XAF
TZS
BND
MYR
LRD
KPW
PHP
IQD
CRC
UZS
TND
DJF
DOP
GIP
XUA
CLF
MDL
NAD
PEN
CDF
USD
BGN
STN
UAH
EUR
FKP
GHS
BSD
CAD
FJD
SRD
KHR
MXN
HUF
UGX
AED
XPF
DZD
RUB
KZT
AZN
KMF
AMD
VND
USN
BMD
ANG
KWD
TJS
LYD
DKK
ERN
ISK
MZN
SHP
}
Enum role {
owner
admin
viewer
}
Enum action {
allow
deny
}
Enum source {
noop
csv
universal_analytics
google_analytics_4
}
Enum site_imports_status {
pending
failed
completed
importing
}
Enum subscriptions_status {
active
deleted
past_due
paused
}
Enum theme {
system
light
dark
}
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table fun_with_flags_toggles {
id integer [pk]
flag_name varchar
gate_type varchar
target varchar
enabled boolean
}
Table oban_jobs {
id integer [pk]
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
}
Table events_v2 {
name unknown
site_id unknown
hostname varchar
pathname varchar
user_id unknown
session_id unknown
timestamp timestamp
"meta.key" array
"meta.value" array
revenue_source_amount unknown
revenue_source_currency unknown
revenue_reporting_amount unknown
revenue_reporting_currency unknown
referrer varchar
referrer_source varchar
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
}
Table sessions_v2 {
hostname varchar
site_id unknown
user_id unknown
session_id unknown
start timestamp
duration unknown
is_bounce unknown
entry_page varchar
exit_page varchar
exit_page_hostname varchar
pageviews unknown
events unknown
sign unknown
"entry_meta.key" array
"entry_meta.value" array
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
referrer varchar
referrer_source varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
timestamp timestamp
transferred_from varchar
}
Table funnels {
id integer [pk]
name varchar
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table goals {
id integer [pk]
event_name varchar
page_path varchar
currency currency
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table sites {
id integer [pk]
domain varchar
timezone varchar
public boolean
locked boolean
stats_start_date date
native_stats_start_at timestamp
allowed_event_props array
conversions_enabled boolean
props_enabled boolean
funnels_enabled boolean
ingest_rate_limit_scale_seconds integer
ingest_rate_limit_threshold integer
domain_changed_from varchar
domain_changed_at timestamp
imported_data jsonb
inserted_at timestamp
updated_at timestamp
}
Table api_keys {
id integer [pk]
name varchar
scopes array
hourly_request_limit integer
key_hash varchar
key_prefix varchar
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table email_activation_codes {
id integer [pk]
code varchar
issued_at timestamp
user_id integer
}
Table invitations {
id integer [pk]
invitation_id varchar
email varchar
role role
inviter_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table totp_recovery_codes {
id integer [pk]
code_digest varchar
user_id integer
inserted_at timestamp
}
Table users {
id integer [pk]
email varchar
password_hash varchar
name varchar
last_seen timestamp
trial_expiry_date date
theme theme
email_verified boolean
previous_email varchar
accept_traffic_until date
allow_next_upgrade_override boolean
totp_enabled boolean
totp_secret bytea
totp_token varchar
totp_last_used_at timestamp
grace_period jsonb
inserted_at timestamp
updated_at timestamp
}
Table enterprise_plans {
id integer [pk]
paddle_plan_id varchar
billing_interval billing_interval
monthly_pageview_limit integer
site_limit integer
team_member_limit integer
features array
hourly_api_request_limit integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table subscriptions {
id integer [pk]
paddle_subscription_id varchar
paddle_plan_id varchar
update_url varchar
cancel_url varchar
status subscriptions_status
next_bill_amount varchar
next_bill_date date
last_bill_date date
currency_code varchar
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table domains_lookup {
site_id unknown
domain varchar
}
Table funnel_steps {
id integer [pk]
step_order integer
funnel_id integer
goal_id integer
inserted_at timestamp
updated_at timestamp
}
Table imported_browsers {
site_id unknown
import_id unknown
date date
browser varchar
browser_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_devices {
site_id unknown
import_id unknown
date date
device varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_entry_pages {
site_id unknown
import_id unknown
date date
entry_page varchar
visitors unknown
entrances unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_exit_pages {
site_id unknown
import_id unknown
date date
exit_page varchar
exits unknown
visitors unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_locations {
site_id unknown
import_id unknown
date date
country varchar
region varchar
city unknown
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_operating_systems {
site_id unknown
import_id unknown
date date
operating_system varchar
operating_system_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_pages {
site_id unknown
import_id unknown
date date
hostname varchar
page varchar
visits unknown
visitors unknown
active_visitors unknown
pageviews unknown
exits unknown
time_on_page unknown
}
Table site_imports {
id integer [pk]
start_date date
end_date date
label varchar
source source
status site_imports_status
legacy boolean
site_id integer
imported_by_id integer
inserted_at timestamp
updated_at timestamp
}
Table imported_sources {
site_id unknown
import_id unknown
date date
source varchar
referrer varchar
utm_source varchar
utm_medium varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_visitors {
site_id unknown
import_id unknown
date date
visitors unknown
pageviews unknown
bounces unknown
visits unknown
visit_duration unknown
}
Table ingest_counters {
event_timebucket timestamp
site_id unknown
domain unknown
metric unknown
value unknown
}
Table plugins_api_tokens {
id uuid [pk]
inserted_at timestamp
updated_at timestamp
token_hash bytea
description varchar
hint varchar
last_used_at timestamp
site_id integer
}
Table shield_rules_country {
id uuid [pk]
site_id integer
country_code varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_hostname {
id uuid [pk]
site_id integer
hostname varchar
hostname_pattern varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_ip {
id uuid [pk]
site_id integer
inet inet
action action
description varchar
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_page {
id uuid [pk]
site_id integer
page_path varchar
page_path_pattern varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table google_auth {
id integer [pk]
email varchar
property varchar
refresh_token varchar
access_token varchar
expires timestamp
user_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table site_memberships {
id integer [pk]
role role
site_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table monthly_reports {
id integer [pk]
recipients array
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table shared_links {
id integer [pk]
site_id integer
name varchar
slug varchar
password_hash varchar
inserted_at timestamp
updated_at timestamp
}
Table spike_notifications {
id integer [pk]
recipients array
threshold integer
last_sent timestamp
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table site_user_preferences {
id integer [pk]
pinned_at timestamp
user_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table weekly_reports {
id integer [pk]
recipients array
site_id integer
inserted_at timestamp
updated_at timestamp
}
Ref: funnels.id < funnel_steps.funnel_id
Ref: goals.id < funnel_steps.goal_id
Ref: sites.id < funnels.site_id
Ref: sites.id < goals.site_id
Ref: sites.id - google_auth.site_id
Ref: sites.id < invitations.site_id
Ref: sites.id - monthly_reports.site_id
Ref: sites.id < plugins_api_tokens.site_id
Ref: sites.id < shared_links.site_id
Ref: sites.id < shield_rules_country.site_id
Ref: sites.id < shield_rules_hostname.site_id
Ref: sites.id < shield_rules_ip.site_id
Ref: sites.id < shield_rules_page.site_id
Ref: sites.id < site_imports.site_id
Ref: sites.id - site_memberships.site_id
Ref: sites.id < site_user_preferences.site_id
Ref: sites.id - spike_notifications.site_id
Ref: sites.id - weekly_reports.site_id
Ref: users.id < api_keys.user_id
Ref: users.id < email_activation_codes.user_id
Ref: users.id - enterprise_plans.user_id
Ref: users.id - google_auth.user_id
Ref: users.id < invitations.inviter_id
Ref: users.id < site_imports.imported_by_id
Ref: users.id < site_memberships.user_id
Ref: users.id < site_user_preferences.user_id
Ref: users.id - subscriptions.user_id
Ref: users.id < totp_recovery_codes.user_id
================================================
FILE: examples/dbml/plausible-analytics/Default.dbml
================================================
Enum billing_interval {
yearly
monthly
}
Enum currency {
KMF
AUD
SAR
BWP
BBD
EGP
YER
CDF
IQD
MRU
JOD
XPT
XBB
NGN
BDT
CNY
ANG
GTQ
HTG
TWD
OMR
STN
AOA
MUR
XCD
TND
THB
KES
GIP
MZN
ERN
MAD
FKP
MVR
BND
KZT
EUR
SYP
MYR
RSD
KRW
COU
GMD
ILS
BAM
XAG
AZN
AFN
AWG
SOS
PAB
AED
UYI
BTN
USN
KPW
IDR
XPD
MOP
GEL
MXV
CHW
XAF
UGX
DJF
SGD
PGK
IRR
VES
PHP
SSP
BOB
XDR
JPY
BHD
UAH
ZAR
BSD
TMT
XOF
XTS
MNT
XSU
XPF
TTD
PLN
AMD
SBD
LSL
GBP
DOP
SEK
MDL
CUP
CZK
SZL
COP
XAU
NOK
CLF
RWF
NAD
KHR
TRY
LAK
SDG
XXX
PEN
LBP
BZD
CLP
KGS
TZS
GNF
KWD
NZD
SVC
LRD
CHF
PKR
SHP
XUA
XBD
LYD
BIF
JMD
ALL
BYN
CUC
UZS
MKD
ZWL
RON
NIO
MMK
SRD
ETB
ARS
GHS
XBA
XBC
UYW
HKD
ISK
DZD
MWK
RUB
SLL
SCR
CHE
CVE
VND
ZMW
HNL
HUF
INR
DKK
FJD
HRK
UYU
PYG
BMD
KYD
VUV
BGN
TOP
MXN
CAD
MGA
BOV
BRL
WST
NPR
CRC
GYD
TJS
LKR
QAR
USD
}
Enum role {
owner
admin
viewer
}
Enum action {
allow
deny
}
Enum source {
noop
csv
universal_analytics
google_analytics_4
}
Enum site_imports_status {
pending
failed
completed
importing
}
Enum subscriptions_status {
active
deleted
past_due
paused
}
Enum theme {
system
light
dark
}
Table schema_migrations {
version integer [pk]
inserted_at timestamp
}
Table fun_with_flags_toggles {
id integer [pk]
flag_name varchar
gate_type varchar
target varchar
enabled boolean
}
Table oban_jobs {
id integer [pk]
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
}
Table api_keys {
id integer [pk]
name varchar
scopes array
hourly_request_limit integer
key_hash varchar
key_prefix varchar
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table email_activation_codes {
id integer [pk]
code varchar
issued_at timestamp
user_id integer
}
Table invitations {
id integer [pk]
invitation_id varchar
email varchar
role role
inviter_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table totp_recovery_codes {
id integer [pk]
code_digest varchar
user_id integer
inserted_at timestamp
}
Table users {
id integer [pk]
email varchar
password_hash varchar
name varchar
last_seen timestamp
trial_expiry_date date
theme theme
email_verified boolean
previous_email varchar
accept_traffic_until date
allow_next_upgrade_override boolean
totp_enabled boolean
totp_secret bytea
totp_token varchar
totp_last_used_at timestamp
grace_period jsonb
inserted_at timestamp
updated_at timestamp
}
Table enterprise_plans {
id integer [pk]
paddle_plan_id varchar
billing_interval billing_interval
monthly_pageview_limit integer
site_limit integer
team_member_limit integer
features array
hourly_api_request_limit integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table subscriptions {
id integer [pk]
paddle_subscription_id varchar
paddle_plan_id varchar
update_url varchar
cancel_url varchar
status subscriptions_status
next_bill_amount varchar
next_bill_date date
last_bill_date date
currency_code varchar
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table events_v2 {
name unknown
site_id unknown
hostname varchar
pathname varchar
user_id unknown
session_id unknown
timestamp timestamp
"meta.key" array
"meta.value" array
revenue_source_amount unknown
revenue_source_currency unknown
revenue_reporting_amount unknown
revenue_reporting_currency unknown
referrer varchar
referrer_source varchar
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
}
Table sessions_v2 {
hostname varchar
site_id unknown
user_id unknown
session_id unknown
start timestamp
duration unknown
is_bounce unknown
entry_page varchar
exit_page varchar
exit_page_hostname varchar
pageviews unknown
events unknown
sign unknown
"entry_meta.key" array
"entry_meta.value" array
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
referrer varchar
referrer_source varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
timestamp timestamp
transferred_from varchar
}
Table domains_lookup {
site_id unknown
domain varchar
}
Table funnels {
id integer [pk]
name varchar
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table funnel_steps {
id integer [pk]
step_order integer
funnel_id integer
goal_id integer
inserted_at timestamp
updated_at timestamp
}
Table goals {
id integer [pk]
event_name varchar
page_path varchar
currency currency
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table imported_browsers {
site_id unknown
import_id unknown
date date
browser varchar
browser_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_devices {
site_id unknown
import_id unknown
date date
device varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_entry_pages {
site_id unknown
import_id unknown
date date
entry_page varchar
visitors unknown
entrances unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_exit_pages {
site_id unknown
import_id unknown
date date
exit_page varchar
exits unknown
visitors unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_locations {
site_id unknown
import_id unknown
date date
country varchar
region varchar
city unknown
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_operating_systems {
site_id unknown
import_id unknown
date date
operating_system varchar
operating_system_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_pages {
site_id unknown
import_id unknown
date date
hostname varchar
page varchar
visits unknown
visitors unknown
active_visitors unknown
pageviews unknown
exits unknown
time_on_page unknown
}
Table site_imports {
id integer [pk]
start_date date
end_date date
label varchar
source source
status site_imports_status
legacy boolean
site_id integer
imported_by_id integer
inserted_at timestamp
updated_at timestamp
}
Table imported_sources {
site_id unknown
import_id unknown
date date
source varchar
referrer varchar
utm_source varchar
utm_medium varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
}
Table imported_visitors {
site_id unknown
import_id unknown
date date
visitors unknown
pageviews unknown
bounces unknown
visits unknown
visit_duration unknown
}
Table ingest_counters {
event_timebucket timestamp
site_id unknown
domain unknown
metric unknown
value unknown
}
Table plugins_api_tokens {
id uuid [pk]
inserted_at timestamp
updated_at timestamp
token_hash bytea
description varchar
hint varchar
last_used_at timestamp
site_id integer
}
Table shield_rules_country {
id uuid [pk]
site_id integer
country_code varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_hostname {
id uuid [pk]
site_id integer
hostname varchar
hostname_pattern varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_ip {
id uuid [pk]
site_id integer
inet inet
action action
description varchar
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table shield_rules_page {
id uuid [pk]
site_id integer
page_path varchar
page_path_pattern varchar
action action
added_by varchar
inserted_at timestamp
updated_at timestamp
}
Table sites {
id integer [pk]
domain varchar
timezone varchar
public boolean
locked boolean
stats_start_date date
native_stats_start_at timestamp
allowed_event_props array
conversions_enabled boolean
props_enabled boolean
funnels_enabled boolean
ingest_rate_limit_scale_seconds integer
ingest_rate_limit_threshold integer
domain_changed_from varchar
domain_changed_at timestamp
imported_data jsonb
inserted_at timestamp
updated_at timestamp
}
Table google_auth {
id integer [pk]
email varchar
property varchar
refresh_token varchar
access_token varchar
expires timestamp
user_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table site_memberships {
id integer [pk]
role role
site_id integer
user_id integer
inserted_at timestamp
updated_at timestamp
}
Table monthly_reports {
id integer [pk]
recipients array
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table shared_links {
id integer [pk]
site_id integer
name varchar
slug varchar
password_hash varchar
inserted_at timestamp
updated_at timestamp
}
Table spike_notifications {
id integer [pk]
recipients array
threshold integer
last_sent timestamp
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table site_user_preferences {
id integer [pk]
pinned_at timestamp
user_id integer
site_id integer
inserted_at timestamp
updated_at timestamp
}
Table weekly_reports {
id integer [pk]
recipients array
site_id integer
inserted_at timestamp
updated_at timestamp
}
Ref: funnels.id < funnel_steps.funnel_id
Ref: goals.id < funnel_steps.goal_id
Ref: sites.id < funnels.site_id
Ref: sites.id < goals.site_id
Ref: sites.id - google_auth.site_id
Ref: sites.id < invitations.site_id
Ref: sites.id - monthly_reports.site_id
Ref: sites.id < plugins_api_tokens.site_id
Ref: sites.id < shared_links.site_id
Ref: sites.id < shield_rules_country.site_id
Ref: sites.id < shield_rules_hostname.site_id
Ref: sites.id < shield_rules_ip.site_id
Ref: sites.id < shield_rules_page.site_id
Ref: sites.id < site_imports.site_id
Ref: sites.id - site_memberships.site_id
Ref: sites.id < site_user_preferences.site_id
Ref: sites.id - spike_notifications.site_id
Ref: sites.id - weekly_reports.site_id
Ref: users.id < api_keys.user_id
Ref: users.id < email_activation_codes.user_id
Ref: users.id - enterprise_plans.user_id
Ref: users.id - google_auth.user_id
Ref: users.id < invitations.inviter_id
Ref: users.id < site_imports.imported_by_id
Ref: users.id < site_memberships.user_id
Ref: users.id < site_user_preferences.user_id
Ref: users.id - subscriptions.user_id
Ref: users.id < totp_recovery_codes.user_id
================================================
FILE: examples/dot/changelog.com/Clusters.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Changelog.Feed" [label= <
| Changelog.Feed |
| feeds |
| :id :id |
| :name :string |
| :slug :string |
| :description :string |
| :title_format :string |
| :plusplus :boolean |
| :autosub :boolean |
| :starts_at :utc_datetime |
| :cover Changelog.Files.Cover.Type |
| :podcast_ids {:array, :integer} |
| :person_ids {:array, :integer} |
| :owner_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Subscription" [label= <| Changelog.Subscription |
| subscriptions |
| :id :id |
| :unsubscribed_at :utc_datetime |
| :context :string |
| :episode_id :id |
| :item_id :id |
| :person_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Topic" [label= <| Changelog.Topic |
| topics |
| :id :id |
| :name :string |
| :slug :string |
| :description :string |
| :website :string |
| :twitter_handle :string |
| :icon Changelog.Files.Icon.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
"Oban.Job" [label= <| Oban.Job |
| oban_jobs |
| :id :id |
| :state :string |
| :queue :string |
| :worker :string |
| :args :map |
| :meta :map |
| :tags {:array, :string} |
| :errors {:array, :map} |
| :attempt :integer |
| :attempted_by {:array, :string} |
| :max_attempts :integer |
| :priority :integer |
| :attempted_at :utc_datetime_usec |
| :cancelled_at :utc_datetime_usec |
| :completed_at :utc_datetime_usec |
| :discarded_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :scheduled_at :utc_datetime_usec |
>]
subgraph cluster_EPISODE {
style=filled
fontname="Roboto Mono"
color = "#b4eeb4"
label = <EPISODE>
"Changelog.Episode" [label= <| Changelog.Episode |
| episodes |
| :id :id |
| :slug :string |
| :guid :string |
| :title :string |
| :subtitle :string |
| :type Changelog.Episode.Type |
| :featured :boolean |
| :highlight :string |
| :subhighlight :string |
| :summary :string |
| :notes :string |
| :doc_url :string |
| :socialize_url :string |
| :published :boolean |
| :published_at :utc_datetime |
| :recorded_at :utc_datetime |
| :recorded_live :boolean |
| :youtube_id :string |
| :cover Changelog.Files.Cover.Type |
| :audio_file Changelog.Files.Audio.Type |
| :audio_bytes :integer |
| :audio_duration :integer |
| :audio_chapters #Ecto.Embedded<[many: Changelog.EpisodeChapter]> |
| :plusplus_file Changelog.Files.PlusPlus.Type |
| :plusplus_bytes :integer |
| :plusplus_duration :integer |
| :plusplus_chapters #Ecto.Embedded<[many: Changelog.EpisodeChapter]> |
| :download_count :float |
| :import_count :float |
| :reach_count :integer |
| :email_subject :string |
| :email_teaser :string |
| :email_content :string |
| :email_sends :integer |
| :email_opens :integer |
| :transcript {:array, :map} |
| :podcast_id :id |
| :request_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeChapter" [label= <| Changelog.EpisodeChapter |
| :id :binary_id |
| :title :string |
| :starts_at :float |
| :ends_at :float |
| :link_url :string |
| :image_url :string |
>]
"Changelog.EpisodeGuest" [label= <| Changelog.EpisodeGuest |
| episode_guests |
| :id :id |
| :position :integer |
| :thanks :boolean |
| :discount_code :string |
| :episode_id :id |
| :person_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeHost" [label= <| Changelog.EpisodeHost |
| episode_hosts |
| :id :id |
| :position :integer |
| :person_id :id |
| :episode_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeRequest" [label= <| Changelog.EpisodeRequest |
| episode_requests |
| :id :id |
| :status Changelog.EpisodeRequest.Status |
| :hosts :string |
| :guests :string |
| :topics :string |
| :pitch :string |
| :pronunciation :string |
| :message :string |
| :podcast_id :id |
| :submitter_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeSponsor" [label= <| Changelog.EpisodeSponsor |
| episode_sponsors |
| :id :id |
| :position :integer |
| :title :string |
| :link_url :string |
| :description :string |
| :starts_at :float |
| :ends_at :float |
| :episode_id :id |
| :sponsor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeStat" [label= <| Changelog.EpisodeStat |
| episode_stats |
| :id :id |
| :date :date |
| :episode_bytes :integer |
| :total_bytes :integer |
| :downloads :float |
| :uniques :integer |
| :demographics :map |
| :episode_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeTopic" [label= <| Changelog.EpisodeTopic |
| episode_topics |
| :id :id |
| :position :integer |
| :topic_id :id |
| :episode_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph cluster_NEWS {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <NEWS>
"Changelog.NewsAd" [label= <| Changelog.NewsAd |
| news_ads |
| :id :id |
| :url :string |
| :headline :string |
| :story :string |
| :image Changelog.Files.Image.Type |
| :active :boolean |
| :newsletter :boolean |
| :impression_count :integer |
| :click_count :integer |
| :sponsorship_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssue" [label= <| Changelog.NewsIssue |
| news_issues |
| :id :id |
| :slug :string |
| :note :string |
| :teaser :string |
| :published :boolean |
| :published_at :utc_datetime |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssueAd" [label= <| Changelog.NewsIssueAd |
| news_issue_ads |
| :id :id |
| :position :integer |
| :image :boolean |
| :ad_id :id |
| :issue_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssueItem" [label= <| Changelog.NewsIssueItem |
| news_issue_items |
| :id :id |
| :position :integer |
| :image :boolean |
| :issue_id :id |
| :item_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItem" [label= <| Changelog.NewsItem |
| news_items |
| :id :id |
| :status Changelog.NewsItem.Status |
| :type Changelog.NewsItem.Type |
| :url :string |
| :headline :string |
| :story :string |
| :image Changelog.Files.Image.Type |
| :object_id :string |
| :feed_only :boolean |
| :pinned :boolean |
| :published_at :utc_datetime |
| :refreshed_at :utc_datetime |
| :impression_count :integer |
| :click_count :integer |
| :message :string |
| :author_id :id |
| :logger_id :id |
| :submitter_id :id |
| :source_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItemComment" [label= <| Changelog.NewsItemComment |
| news_item_comments |
| :id :id |
| :content :string |
| :approved :boolean |
| :edited_at :utc_datetime |
| :deleted_at :utc_datetime |
| :item_id :id |
| :author_id :id |
| :parent_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItemTopic" [label= <| Changelog.NewsItemTopic |
| news_item_topics |
| :id :id |
| :position :integer |
| :item_id :id |
| :topic_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsQueue" [label= <| Changelog.NewsQueue |
| news_queue |
| :id :id |
| :position :float |
| :item_id :id |
>]
"Changelog.NewsSource" [label= <| Changelog.NewsSource |
| news_sources |
| :id :id |
| :name :string |
| :slug :string |
| :website :string |
| :twitter_handle :string |
| :description :string |
| :feed :string |
| :regex :string |
| :publication :boolean |
| :icon Changelog.Files.Icon.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsSponsorship" [label= <| Changelog.NewsSponsorship |
| news_sponsorships |
| :id :id |
| :name :string |
| :weeks {:array, :date} |
| :impression_count :integer |
| :click_count :integer |
| :sponsor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph cluster_PERSON {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <PERSON>
"Changelog.Person" [label= <| Changelog.Person |
| people |
| :id :id |
| :name :string |
| :email :string |
| :handle :string |
| :github_handle :string |
| :linkedin_handle :string |
| :mastodon_handle :string |
| :twitter_handle :string |
| :slack_id :string |
| :website :string |
| :bio :string |
| :location :string |
| :auth_token :string |
| :auth_token_expires_at :utc_datetime |
| :joined_at :utc_datetime |
| :signed_in_at :utc_datetime |
| :approved :boolean |
| :avatar Changelog.Files.Avatar.Type |
| :admin :boolean |
| :host :boolean |
| :editor :boolean |
| :public_profile :boolean |
| :settings #Ecto.Embedded<[one: Changelog.Person.Settings]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Person.Settings" [label= <| Changelog.Person.Settings |
| :subscribe_to_contributed_news :boolean |
| :subscribe_to_participated_episodes :boolean |
| :email_on_authored_news :boolean |
| :email_on_submitted_news :boolean |
| :email_on_comment_replies :boolean |
| :email_on_comment_mentions :boolean |
>]
}
subgraph cluster_PODCAST {
style=filled
fontname="Roboto Mono"
color = "#ffefd5"
label = <PODCAST>
"Changelog.Podcast" [label= <| Changelog.Podcast |
| podcasts |
| :id :id |
| :name :string |
| :slug :string |
| :status Changelog.Podcast.Status |
| :welcome :string |
| :description :string |
| :extended_description :string |
| :vanity_domain :string |
| :keywords :string |
| :mastodon_handle :string |
| :twitter_handle :string |
| :apple_url :string |
| :spotify_url :string |
| :riverside_url :string |
| :chartable_id :string |
| :schedule_note :string |
| :download_count :float |
| :reach_count :integer |
| :recorded_live :boolean |
| :partner :boolean |
| :position :integer |
| :subscribers :map |
| :cover Changelog.Files.Cover.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PodcastHost" [label= <| Changelog.PodcastHost |
| podcast_hosts |
| :id :id |
| :position :integer |
| :retired :boolean |
| :person_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PodcastTopic" [label= <| Changelog.PodcastTopic |
| podcast_topics |
| :id :id |
| :position :integer |
| :podcast_id :id |
| :topic_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph cluster_POST {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <POST>
"Changelog.Post" [label= <| Changelog.Post |
| posts |
| :id :id |
| :title :string |
| :subtitle :string |
| :slug :string |
| :guid :string |
| :canonical_url :string |
| :image Changelog.Files.Image.Type |
| :tldr :string |
| :body :string |
| :published :boolean |
| :published_at :utc_datetime |
| :author_id :id |
| :editor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PostTopic" [label= <| Changelog.PostTopic |
| post_topics |
| :id :id |
| :position :integer |
| :topic_id :id |
| :post_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph cluster_SPONSOR {
style=filled
fontname="Roboto Mono"
color = "#fffafa"
label = <SPONSOR>
"Changelog.Sponsor" [label= <| Changelog.Sponsor |
| sponsors |
| :id :id |
| :name :string |
| :description :string |
| :website :string |
| :github_handle :string |
| :twitter_handle :string |
| :avatar Changelog.Files.Avatar.Type |
| :color_logo Changelog.Files.ColorLogo.Type |
| :dark_logo Changelog.Files.DarkLogo.Type |
| :light_logo Changelog.Files.LightLogo.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.SponsorRep" [label= <| Changelog.SponsorRep |
| sponsor_reps |
| :id :id |
| :sponsor_id :id |
| :person_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
"Changelog.EpisodeRequest":"field@id":e -> "Changelog.Episode":"field@request_id":w [dir=none]
"Changelog.Episode":"field@audio_chapters":e -> "Changelog.EpisodeChapter":"header@schema_module":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeGuest":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeHost":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeSponsor":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeStat":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeTopic":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.Subscription":"field@episode_id":w
"Changelog.Episode":"field@plusplus_chapters":e -> "Changelog.EpisodeChapter":"header@schema_module":w
"Changelog.NewsAd":"field@id":e -> "Changelog.NewsIssueAd":"field@ad_id":w
"Changelog.NewsIssue":"field@id":e -> "Changelog.NewsIssueAd":"field@issue_id":w
"Changelog.NewsIssue":"field@id":e -> "Changelog.NewsIssueItem":"field@issue_id":w
"Changelog.NewsItemComment":"field@id":e -> "Changelog.NewsItemComment":"field@parent_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsIssueItem":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsItemComment":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsItemTopic":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsQueue":"field@item_id":w [dir=none]
"Changelog.NewsItem":"field@id":e -> "Changelog.Subscription":"field@item_id":w
"Changelog.NewsSource":"field@id":e -> "Changelog.NewsItem":"field@source_id":w
"Changelog.NewsSponsorship":"field@id":e -> "Changelog.NewsAd":"field@sponsorship_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeGuest":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeHost":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeRequest":"field@submitter_id":w
"Changelog.Person":"field@id":e -> "Changelog.Feed":"field@owner_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItemComment":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@logger_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@submitter_id":w
"Changelog.Person":"field@id":e -> "Changelog.PodcastHost":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.Post":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.Post":"field@editor_id":w
"Changelog.Person":"field@id":e -> "Changelog.SponsorRep":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.Subscription":"field@person_id":w
"Changelog.Person":"field@settings":e -> "Changelog.Person.Settings":"header@schema_module":w [dir=none]
"Changelog.Podcast":"field@id":e -> "Changelog.EpisodeRequest":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.EpisodeStat":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.Episode":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.PodcastHost":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.PodcastTopic":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.Subscription":"field@podcast_id":w
"Changelog.Post":"field@id":e -> "Changelog.PostTopic":"field@post_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.EpisodeSponsor":"field@sponsor_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.NewsSponsorship":"field@sponsor_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.SponsorRep":"field@sponsor_id":w
"Changelog.Topic":"field@id":e -> "Changelog.EpisodeTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.NewsItemTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.PodcastTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.PostTopic":"field@topic_id":w
}
================================================
FILE: examples/dot/changelog.com/Default.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Changelog.Episode" [label= <| Changelog.Episode |
| episodes |
| :id :id |
| :slug :string |
| :guid :string |
| :title :string |
| :subtitle :string |
| :type Changelog.Episode.Type |
| :featured :boolean |
| :highlight :string |
| :subhighlight :string |
| :summary :string |
| :notes :string |
| :doc_url :string |
| :socialize_url :string |
| :published :boolean |
| :published_at :utc_datetime |
| :recorded_at :utc_datetime |
| :recorded_live :boolean |
| :youtube_id :string |
| :cover Changelog.Files.Cover.Type |
| :audio_file Changelog.Files.Audio.Type |
| :audio_bytes :integer |
| :audio_duration :integer |
| :audio_chapters #Ecto.Embedded<[many: Changelog.EpisodeChapter]> |
| :plusplus_file Changelog.Files.PlusPlus.Type |
| :plusplus_bytes :integer |
| :plusplus_duration :integer |
| :plusplus_chapters #Ecto.Embedded<[many: Changelog.EpisodeChapter]> |
| :download_count :float |
| :import_count :float |
| :reach_count :integer |
| :email_subject :string |
| :email_teaser :string |
| :email_content :string |
| :email_sends :integer |
| :email_opens :integer |
| :transcript {:array, :map} |
| :podcast_id :id |
| :request_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeChapter" [label= <| Changelog.EpisodeChapter |
| :id :binary_id |
| :title :string |
| :starts_at :float |
| :ends_at :float |
| :link_url :string |
| :image_url :string |
>]
"Changelog.EpisodeGuest" [label= <| Changelog.EpisodeGuest |
| episode_guests |
| :id :id |
| :position :integer |
| :thanks :boolean |
| :discount_code :string |
| :episode_id :id |
| :person_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeHost" [label= <| Changelog.EpisodeHost |
| episode_hosts |
| :id :id |
| :position :integer |
| :person_id :id |
| :episode_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeRequest" [label= <| Changelog.EpisodeRequest |
| episode_requests |
| :id :id |
| :status Changelog.EpisodeRequest.Status |
| :hosts :string |
| :guests :string |
| :topics :string |
| :pitch :string |
| :pronunciation :string |
| :message :string |
| :podcast_id :id |
| :submitter_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeSponsor" [label= <| Changelog.EpisodeSponsor |
| episode_sponsors |
| :id :id |
| :position :integer |
| :title :string |
| :link_url :string |
| :description :string |
| :starts_at :float |
| :ends_at :float |
| :episode_id :id |
| :sponsor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeStat" [label= <| Changelog.EpisodeStat |
| episode_stats |
| :id :id |
| :date :date |
| :episode_bytes :integer |
| :total_bytes :integer |
| :downloads :float |
| :uniques :integer |
| :demographics :map |
| :episode_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.EpisodeTopic" [label= <| Changelog.EpisodeTopic |
| episode_topics |
| :id :id |
| :position :integer |
| :topic_id :id |
| :episode_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Feed" [label= <| Changelog.Feed |
| feeds |
| :id :id |
| :name :string |
| :slug :string |
| :description :string |
| :title_format :string |
| :plusplus :boolean |
| :autosub :boolean |
| :starts_at :utc_datetime |
| :cover Changelog.Files.Cover.Type |
| :podcast_ids {:array, :integer} |
| :person_ids {:array, :integer} |
| :owner_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsAd" [label= <| Changelog.NewsAd |
| news_ads |
| :id :id |
| :url :string |
| :headline :string |
| :story :string |
| :image Changelog.Files.Image.Type |
| :active :boolean |
| :newsletter :boolean |
| :impression_count :integer |
| :click_count :integer |
| :sponsorship_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssue" [label= <| Changelog.NewsIssue |
| news_issues |
| :id :id |
| :slug :string |
| :note :string |
| :teaser :string |
| :published :boolean |
| :published_at :utc_datetime |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssueAd" [label= <| Changelog.NewsIssueAd |
| news_issue_ads |
| :id :id |
| :position :integer |
| :image :boolean |
| :ad_id :id |
| :issue_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsIssueItem" [label= <| Changelog.NewsIssueItem |
| news_issue_items |
| :id :id |
| :position :integer |
| :image :boolean |
| :issue_id :id |
| :item_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItem" [label= <| Changelog.NewsItem |
| news_items |
| :id :id |
| :status Changelog.NewsItem.Status |
| :type Changelog.NewsItem.Type |
| :url :string |
| :headline :string |
| :story :string |
| :image Changelog.Files.Image.Type |
| :object_id :string |
| :feed_only :boolean |
| :pinned :boolean |
| :published_at :utc_datetime |
| :refreshed_at :utc_datetime |
| :impression_count :integer |
| :click_count :integer |
| :message :string |
| :author_id :id |
| :logger_id :id |
| :submitter_id :id |
| :source_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItemComment" [label= <| Changelog.NewsItemComment |
| news_item_comments |
| :id :id |
| :content :string |
| :approved :boolean |
| :edited_at :utc_datetime |
| :deleted_at :utc_datetime |
| :item_id :id |
| :author_id :id |
| :parent_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsItemTopic" [label= <| Changelog.NewsItemTopic |
| news_item_topics |
| :id :id |
| :position :integer |
| :item_id :id |
| :topic_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsQueue" [label= <| Changelog.NewsQueue |
| news_queue |
| :id :id |
| :position :float |
| :item_id :id |
>]
"Changelog.NewsSource" [label= <| Changelog.NewsSource |
| news_sources |
| :id :id |
| :name :string |
| :slug :string |
| :website :string |
| :twitter_handle :string |
| :description :string |
| :feed :string |
| :regex :string |
| :publication :boolean |
| :icon Changelog.Files.Icon.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.NewsSponsorship" [label= <| Changelog.NewsSponsorship |
| news_sponsorships |
| :id :id |
| :name :string |
| :weeks {:array, :date} |
| :impression_count :integer |
| :click_count :integer |
| :sponsor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Person" [label= <| Changelog.Person |
| people |
| :id :id |
| :name :string |
| :email :string |
| :handle :string |
| :github_handle :string |
| :linkedin_handle :string |
| :mastodon_handle :string |
| :twitter_handle :string |
| :slack_id :string |
| :website :string |
| :bio :string |
| :location :string |
| :auth_token :string |
| :auth_token_expires_at :utc_datetime |
| :joined_at :utc_datetime |
| :signed_in_at :utc_datetime |
| :approved :boolean |
| :avatar Changelog.Files.Avatar.Type |
| :admin :boolean |
| :host :boolean |
| :editor :boolean |
| :public_profile :boolean |
| :settings #Ecto.Embedded<[one: Changelog.Person.Settings]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Person.Settings" [label= <| Changelog.Person.Settings |
| :subscribe_to_contributed_news :boolean |
| :subscribe_to_participated_episodes :boolean |
| :email_on_authored_news :boolean |
| :email_on_submitted_news :boolean |
| :email_on_comment_replies :boolean |
| :email_on_comment_mentions :boolean |
>]
"Changelog.Podcast" [label= <| Changelog.Podcast |
| podcasts |
| :id :id |
| :name :string |
| :slug :string |
| :status Changelog.Podcast.Status |
| :welcome :string |
| :description :string |
| :extended_description :string |
| :vanity_domain :string |
| :keywords :string |
| :mastodon_handle :string |
| :twitter_handle :string |
| :apple_url :string |
| :spotify_url :string |
| :riverside_url :string |
| :chartable_id :string |
| :schedule_note :string |
| :download_count :float |
| :reach_count :integer |
| :recorded_live :boolean |
| :partner :boolean |
| :position :integer |
| :subscribers :map |
| :cover Changelog.Files.Cover.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PodcastHost" [label= <| Changelog.PodcastHost |
| podcast_hosts |
| :id :id |
| :position :integer |
| :retired :boolean |
| :person_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PodcastTopic" [label= <| Changelog.PodcastTopic |
| podcast_topics |
| :id :id |
| :position :integer |
| :podcast_id :id |
| :topic_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Post" [label= <| Changelog.Post |
| posts |
| :id :id |
| :title :string |
| :subtitle :string |
| :slug :string |
| :guid :string |
| :canonical_url :string |
| :image Changelog.Files.Image.Type |
| :tldr :string |
| :body :string |
| :published :boolean |
| :published_at :utc_datetime |
| :author_id :id |
| :editor_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.PostTopic" [label= <| Changelog.PostTopic |
| post_topics |
| :id :id |
| :position :integer |
| :topic_id :id |
| :post_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Sponsor" [label= <| Changelog.Sponsor |
| sponsors |
| :id :id |
| :name :string |
| :description :string |
| :website :string |
| :github_handle :string |
| :twitter_handle :string |
| :avatar Changelog.Files.Avatar.Type |
| :color_logo Changelog.Files.ColorLogo.Type |
| :dark_logo Changelog.Files.DarkLogo.Type |
| :light_logo Changelog.Files.LightLogo.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.SponsorRep" [label= <| Changelog.SponsorRep |
| sponsor_reps |
| :id :id |
| :sponsor_id :id |
| :person_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Subscription" [label= <| Changelog.Subscription |
| subscriptions |
| :id :id |
| :unsubscribed_at :utc_datetime |
| :context :string |
| :episode_id :id |
| :item_id :id |
| :person_id :id |
| :podcast_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Changelog.Topic" [label= <| Changelog.Topic |
| topics |
| :id :id |
| :name :string |
| :slug :string |
| :description :string |
| :website :string |
| :twitter_handle :string |
| :icon Changelog.Files.Icon.Type |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
"Oban.Job" [label= <| Oban.Job |
| oban_jobs |
| :id :id |
| :state :string |
| :queue :string |
| :worker :string |
| :args :map |
| :meta :map |
| :tags {:array, :string} |
| :errors {:array, :map} |
| :attempt :integer |
| :attempted_by {:array, :string} |
| :max_attempts :integer |
| :priority :integer |
| :attempted_at :utc_datetime_usec |
| :cancelled_at :utc_datetime_usec |
| :completed_at :utc_datetime_usec |
| :discarded_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :scheduled_at :utc_datetime_usec |
>]
"Changelog.EpisodeRequest":"field@id":e -> "Changelog.Episode":"field@request_id":w [dir=none]
"Changelog.Episode":"field@audio_chapters":e -> "Changelog.EpisodeChapter":"header@schema_module":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeGuest":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeHost":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeSponsor":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeStat":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.EpisodeTopic":"field@episode_id":w
"Changelog.Episode":"field@id":e -> "Changelog.Subscription":"field@episode_id":w
"Changelog.Episode":"field@plusplus_chapters":e -> "Changelog.EpisodeChapter":"header@schema_module":w
"Changelog.NewsAd":"field@id":e -> "Changelog.NewsIssueAd":"field@ad_id":w
"Changelog.NewsIssue":"field@id":e -> "Changelog.NewsIssueAd":"field@issue_id":w
"Changelog.NewsIssue":"field@id":e -> "Changelog.NewsIssueItem":"field@issue_id":w
"Changelog.NewsItemComment":"field@id":e -> "Changelog.NewsItemComment":"field@parent_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsIssueItem":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsItemComment":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsItemTopic":"field@item_id":w
"Changelog.NewsItem":"field@id":e -> "Changelog.NewsQueue":"field@item_id":w [dir=none]
"Changelog.NewsItem":"field@id":e -> "Changelog.Subscription":"field@item_id":w
"Changelog.NewsSource":"field@id":e -> "Changelog.NewsItem":"field@source_id":w
"Changelog.NewsSponsorship":"field@id":e -> "Changelog.NewsAd":"field@sponsorship_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeGuest":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeHost":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.EpisodeRequest":"field@submitter_id":w
"Changelog.Person":"field@id":e -> "Changelog.Feed":"field@owner_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItemComment":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@logger_id":w
"Changelog.Person":"field@id":e -> "Changelog.NewsItem":"field@submitter_id":w
"Changelog.Person":"field@id":e -> "Changelog.PodcastHost":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.Post":"field@author_id":w
"Changelog.Person":"field@id":e -> "Changelog.Post":"field@editor_id":w
"Changelog.Person":"field@id":e -> "Changelog.SponsorRep":"field@person_id":w
"Changelog.Person":"field@id":e -> "Changelog.Subscription":"field@person_id":w
"Changelog.Person":"field@settings":e -> "Changelog.Person.Settings":"header@schema_module":w [dir=none]
"Changelog.Podcast":"field@id":e -> "Changelog.EpisodeRequest":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.EpisodeStat":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.Episode":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.PodcastHost":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.PodcastTopic":"field@podcast_id":w
"Changelog.Podcast":"field@id":e -> "Changelog.Subscription":"field@podcast_id":w
"Changelog.Post":"field@id":e -> "Changelog.PostTopic":"field@post_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.EpisodeSponsor":"field@sponsor_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.NewsSponsorship":"field@sponsor_id":w
"Changelog.Sponsor":"field@id":e -> "Changelog.SponsorRep":"field@sponsor_id":w
"Changelog.Topic":"field@id":e -> "Changelog.EpisodeTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.NewsItemTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.PodcastTopic":"field@topic_id":w
"Changelog.Topic":"field@id":e -> "Changelog.PostTopic":"field@topic_id":w
}
================================================
FILE: examples/dot/changelog.com/No-fields.dot
================================================
strict digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Changelog.Episode" [label= <| Changelog.Episode |
| episodes |
>]
"Changelog.EpisodeChapter" [label= <>]
"Changelog.EpisodeGuest" [label= <| Changelog.EpisodeGuest |
| episode_guests |
>]
"Changelog.EpisodeHost" [label= <| Changelog.EpisodeHost |
| episode_hosts |
>]
"Changelog.EpisodeRequest" [label= <| Changelog.EpisodeRequest |
| episode_requests |
>]
"Changelog.EpisodeSponsor" [label= <| Changelog.EpisodeSponsor |
| episode_sponsors |
>]
"Changelog.EpisodeStat" [label= <| Changelog.EpisodeStat |
| episode_stats |
>]
"Changelog.EpisodeTopic" [label= <| Changelog.EpisodeTopic |
| episode_topics |
>]
"Changelog.Feed" [label= <>]
"Changelog.NewsAd" [label= <| Changelog.NewsAd |
| news_ads |
>]
"Changelog.NewsIssue" [label= <| Changelog.NewsIssue |
| news_issues |
>]
"Changelog.NewsIssueAd" [label= <| Changelog.NewsIssueAd |
| news_issue_ads |
>]
"Changelog.NewsIssueItem" [label= <| Changelog.NewsIssueItem |
| news_issue_items |
>]
"Changelog.NewsItem" [label= <| Changelog.NewsItem |
| news_items |
>]
"Changelog.NewsItemComment" [label= <| Changelog.NewsItemComment |
| news_item_comments |
>]
"Changelog.NewsItemTopic" [label= <| Changelog.NewsItemTopic |
| news_item_topics |
>]
"Changelog.NewsQueue" [label= <| Changelog.NewsQueue |
| news_queue |
>]
"Changelog.NewsSource" [label= <| Changelog.NewsSource |
| news_sources |
>]
"Changelog.NewsSponsorship" [label= <| Changelog.NewsSponsorship |
| news_sponsorships |
>]
"Changelog.Person" [label= <>]
"Changelog.Person.Settings" [label= <| Changelog.Person.Settings |
>]
"Changelog.Podcast" [label= <| Changelog.Podcast |
| podcasts |
>]
"Changelog.PodcastHost" [label= <| Changelog.PodcastHost |
| podcast_hosts |
>]
"Changelog.PodcastTopic" [label= <| Changelog.PodcastTopic |
| podcast_topics |
>]
"Changelog.Post" [label= <>]
"Changelog.PostTopic" [label= <| Changelog.PostTopic |
| post_topics |
>]
"Changelog.Sponsor" [label= <| Changelog.Sponsor |
| sponsors |
>]
"Changelog.SponsorRep" [label= <| Changelog.SponsorRep |
| sponsor_reps |
>]
"Changelog.Subscription" [label= <| Changelog.Subscription |
| subscriptions |
>]
"Changelog.Topic" [label= <>]
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
>]
"Oban.Job" [label= <>]
"Changelog.EpisodeRequest":e -> "Changelog.Episode":w [dir=none]
"Changelog.Episode":e -> "Changelog.EpisodeChapter":w
"Changelog.Episode":e -> "Changelog.EpisodeGuest":w
"Changelog.Episode":e -> "Changelog.EpisodeHost":w
"Changelog.Episode":e -> "Changelog.EpisodeSponsor":w
"Changelog.Episode":e -> "Changelog.EpisodeStat":w
"Changelog.Episode":e -> "Changelog.EpisodeTopic":w
"Changelog.Episode":e -> "Changelog.Subscription":w
"Changelog.Episode":e -> "Changelog.EpisodeChapter":w
"Changelog.NewsAd":e -> "Changelog.NewsIssueAd":w
"Changelog.NewsIssue":e -> "Changelog.NewsIssueAd":w
"Changelog.NewsIssue":e -> "Changelog.NewsIssueItem":w
"Changelog.NewsItemComment":e -> "Changelog.NewsItemComment":w
"Changelog.NewsItem":e -> "Changelog.NewsIssueItem":w
"Changelog.NewsItem":e -> "Changelog.NewsItemComment":w
"Changelog.NewsItem":e -> "Changelog.NewsItemTopic":w
"Changelog.NewsItem":e -> "Changelog.NewsQueue":w [dir=none]
"Changelog.NewsItem":e -> "Changelog.Subscription":w
"Changelog.NewsSource":e -> "Changelog.NewsItem":w
"Changelog.NewsSponsorship":e -> "Changelog.NewsAd":w
"Changelog.Person":e -> "Changelog.EpisodeGuest":w
"Changelog.Person":e -> "Changelog.EpisodeHost":w
"Changelog.Person":e -> "Changelog.EpisodeRequest":w
"Changelog.Person":e -> "Changelog.Feed":w
"Changelog.Person":e -> "Changelog.NewsItemComment":w
"Changelog.Person":e -> "Changelog.NewsItem":w
"Changelog.Person":e -> "Changelog.NewsItem":w
"Changelog.Person":e -> "Changelog.NewsItem":w
"Changelog.Person":e -> "Changelog.PodcastHost":w
"Changelog.Person":e -> "Changelog.Post":w
"Changelog.Person":e -> "Changelog.Post":w
"Changelog.Person":e -> "Changelog.SponsorRep":w
"Changelog.Person":e -> "Changelog.Subscription":w
"Changelog.Person":e -> "Changelog.Person.Settings":w [dir=none]
"Changelog.Podcast":e -> "Changelog.EpisodeRequest":w
"Changelog.Podcast":e -> "Changelog.EpisodeStat":w
"Changelog.Podcast":e -> "Changelog.Episode":w
"Changelog.Podcast":e -> "Changelog.PodcastHost":w
"Changelog.Podcast":e -> "Changelog.PodcastTopic":w
"Changelog.Podcast":e -> "Changelog.Subscription":w
"Changelog.Post":e -> "Changelog.PostTopic":w
"Changelog.Sponsor":e -> "Changelog.EpisodeSponsor":w
"Changelog.Sponsor":e -> "Changelog.NewsSponsorship":w
"Changelog.Sponsor":e -> "Changelog.SponsorRep":w
"Changelog.Topic":e -> "Changelog.EpisodeTopic":w
"Changelog.Topic":e -> "Changelog.NewsItemTopic":w
"Changelog.Topic":e -> "Changelog.PodcastTopic":w
"Changelog.Topic":e -> "Changelog.PostTopic":w
}
================================================
FILE: examples/dot/hexpm/Contexts-as-clusters-no-fields.dot
================================================
strict digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
subgraph "cluster_Ecto.Migration" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Ecto.Migration>
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
>]
}
subgraph "cluster_Hexpm.Accounts" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Hexpm.Accounts>
"Hexpm.Accounts.AuditLog" [label= <| Hexpm.Accounts.AuditLog |
| audit_logs |
>]
"Hexpm.Accounts.Email" [label= <| Hexpm.Accounts.Email |
| emails |
>]
"Hexpm.Accounts.Key" [label= <>]
"Hexpm.Accounts.Key.Use" [label= <>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
>]
"Hexpm.Accounts.Organization" [label= <| Hexpm.Accounts.Organization |
| organizations |
>]
"Hexpm.Accounts.OrganizationUser" [label= <| Hexpm.Accounts.OrganizationUser |
| organization_users |
>]
"Hexpm.Accounts.PasswordReset" [label= <| Hexpm.Accounts.PasswordReset |
| password_resets |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
>]
"Hexpm.Accounts.Session" [label= <| Hexpm.Accounts.Session |
| sessions |
>]
"Hexpm.Accounts.TFA" [label= <>]
"Hexpm.Accounts.User" [label= <| Hexpm.Accounts.User |
| users |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
>]
}
subgraph "cluster_Hexpm.BlockAddress" {
style=filled
fontname="Roboto Mono"
color = "#fffafa"
label = <Hexpm.BlockAddress>
"Hexpm.BlockAddress.Entry" [label= <| Hexpm.BlockAddress.Entry |
| blocked_addresses |
>]
}
subgraph "cluster_Hexpm.Repository" {
style=filled
fontname="Roboto Mono"
color = "#eedfcc"
label = <Hexpm.Repository>
"Hexpm.Repository.Download" [label= <| Hexpm.Repository.Download |
| downloads |
>]
"Hexpm.Repository.Install" [label= <| Hexpm.Repository.Install |
| installs |
>]
"Hexpm.Repository.Package" [label= <| Hexpm.Repository.Package |
| packages |
>]
"Hexpm.Repository.PackageDependant" [label= <| Hexpm.Repository.PackageDependant |
| package_dependants |
>]
"Hexpm.Repository.PackageDownload" [label= <| Hexpm.Repository.PackageDownload |
| package_downloads |
>]
"Hexpm.Repository.PackageMetadata" [label= <| Hexpm.Repository.PackageMetadata |
>]
"Hexpm.Repository.PackageOwner" [label= <| Hexpm.Repository.PackageOwner |
| package_owners |
>]
"Hexpm.Repository.PackageReport" [label= <| Hexpm.Repository.PackageReport |
| package_reports |
>]
"Hexpm.Repository.PackageReportComment" [label= <| Hexpm.Repository.PackageReportComment |
| package_report_comments |
>]
"Hexpm.Repository.PackageReportRelease" [label= <| Hexpm.Repository.PackageReportRelease |
| package_report_releases |
>]
"Hexpm.Repository.Release" [label= <| Hexpm.Repository.Release |
| releases |
>]
"Hexpm.Repository.ReleaseDownload" [label= <| Hexpm.Repository.ReleaseDownload |
| release_downloads |
>]
"Hexpm.Repository.ReleaseMetadata" [label= <| Hexpm.Repository.ReleaseMetadata |
>]
"Hexpm.Repository.ReleaseRetirement" [label= <| Hexpm.Repository.ReleaseRetirement |
>]
"Hexpm.Repository.Repository" [label= <| Hexpm.Repository.Repository |
| repositories |
>]
"Hexpm.Repository.Requirement" [label= <| Hexpm.Repository.Requirement |
| requirements |
>]
}
subgraph "cluster_Hexpm.ShortURLs" {
style=filled
fontname="Roboto Mono"
color = "#b4eeb4"
label = <Hexpm.ShortURLs>
"Hexpm.ShortURLs.ShortURL" [label= <| Hexpm.ShortURLs.ShortURL |
| short_urls |
>]
}
"Hexpm.Accounts.TFA":e -> "Hexpm.Accounts.RecoveryCode":w
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.Key.Use":w [dir=none]
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.KeyPermission":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.Key":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.OrganizationUser":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Repository.Repository":w [dir=none]
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.User":w [dir=none]
"Hexpm.Repository.PackageReport":e -> "Hexpm.Repository.PackageReportComment":w
"Hexpm.Repository.PackageReport":e -> "Hexpm.Repository.PackageReportRelease":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Download":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageDependant":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageDownload":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageOwner":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageReport":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.ReleaseDownload":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Release":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Requirement":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageMetadata":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.Download":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.PackageReportRelease":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseDownload":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.Requirement":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseMetadata":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseRetirement":w [dir=none]
"Hexpm.Repository.Repository":e -> "Hexpm.Repository.Package":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.UserHandles":w [dir=none]
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.Email":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.Key":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.OrganizationUser":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageOwner":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageReportComment":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageReport":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.PasswordReset":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.Release":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.TFA":w [dir=none]
}
================================================
FILE: examples/dot/hexpm/Contexts-as-clusters.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
subgraph "cluster_Ecto.Migration" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Ecto.Migration>
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
}
subgraph "cluster_Hexpm.Accounts" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Hexpm.Accounts>
"Hexpm.Accounts.AuditLog" [label= <| Hexpm.Accounts.AuditLog |
| audit_logs |
| :id :id |
| :user_agent :string |
| :remote_ip :string |
| :action :string |
| :params :map |
| :user_id :id |
| :organization_id :id |
| :key_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Email" [label= <| Hexpm.Accounts.Email |
| emails |
| :id :id |
| :email :string |
| :verified :boolean |
| :primary :boolean |
| :public :boolean |
| :gravatar :boolean |
| :verification_key :string |
| :verification_expiry :utc_datetime_usec |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Key" [label= <| Hexpm.Accounts.Key |
| keys |
| :id :id |
| :name :string |
| :secret_first :string |
| :secret_second :string |
| :public :boolean |
| :revoke_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :last_use #Ecto.Embedded<[one: Hexpm.Accounts.Key.Use]> |
| :user_id :id |
| :organization_id :id |
| :permissions #Ecto.Embedded<[many: Hexpm.Accounts.KeyPermission]> |
>]
"Hexpm.Accounts.Key.Use" [label= <| Hexpm.Accounts.Key.Use |
| :id :binary_id |
| :used_at :utc_datetime_usec |
| :user_agent :string |
| :ip :string |
>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
| :id :binary_id |
| :domain :string |
| :resource :string |
>]
"Hexpm.Accounts.Organization" [label= <| Hexpm.Accounts.Organization |
| organizations |
| :id :id |
| :name :string |
| :billing_active :boolean |
| :billing_override :boolean |
| :trial_end :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.OrganizationUser" [label= <| Hexpm.Accounts.OrganizationUser |
| organization_users |
| :id :id |
| :role :string |
| :organization_id :id |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.PasswordReset" [label= <| Hexpm.Accounts.PasswordReset |
| password_resets |
| :id :id |
| :key :string |
| :primary_email :string |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
| :id :binary_id |
| :code :string |
| :used_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Session" [label= <| Hexpm.Accounts.Session |
| sessions |
| :id :id |
| :token :binary |
| :data :map |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.TFA" [label= <| Hexpm.Accounts.TFA |
| :secret :string |
| :tfa_enabled :boolean |
| :app_enabled :boolean |
| :recovery_codes #Ecto.Embedded<[many: Hexpm.Accounts.RecoveryCode]> |
>]
"Hexpm.Accounts.User" [label= <| Hexpm.Accounts.User |
| users |
| :id :id |
| :username :string |
| :full_name :string |
| :password :string |
| :service :boolean |
| :deactivated_at :utc_datetime_usec |
| :role :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :handles #Ecto.Embedded<[one: Hexpm.Accounts.UserHandles]> |
| :tfa #Ecto.Embedded<[one: Hexpm.Accounts.TFA]> |
| :organization_id :id |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
| :id :binary_id |
| :twitter :string |
| :github :string |
| :elixirforum :string |
| :freenode :string |
| :slack :string |
>]
}
subgraph "cluster_Hexpm.BlockAddress" {
style=filled
fontname="Roboto Mono"
color = "#fffafa"
label = <Hexpm.BlockAddress>
"Hexpm.BlockAddress.Entry" [label= <| Hexpm.BlockAddress.Entry |
| blocked_addresses |
| :id :id |
| :ip :string |
| :comment :string |
>]
}
subgraph "cluster_Hexpm.Repository" {
style=filled
fontname="Roboto Mono"
color = "#eedfcc"
label = <Hexpm.Repository>
"Hexpm.Repository.Download" [label= <| Hexpm.Repository.Download |
| downloads |
| :id :id |
| :package_id :id |
| :release_id :id |
| :downloads :integer |
| :day :date |
>]
"Hexpm.Repository.Install" [label= <| Hexpm.Repository.Install |
| installs |
| :id :id |
| :hex :string |
| :elixirs {:array, :string} |
>]
"Hexpm.Repository.Package" [label= <| Hexpm.Repository.Package |
| packages |
| :id :id |
| :name :string |
| :docs_updated_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :repository_id :id |
| :meta #Ecto.Embedded<[one: Hexpm.Repository.PackageMetadata]> |
>]
"Hexpm.Repository.PackageDependant" [label= <| Hexpm.Repository.PackageDependant |
| package_dependants |
| :id :id |
| :package_id :id |
| :name :string |
| :repo :string |
>]
"Hexpm.Repository.PackageDownload" [label= <| Hexpm.Repository.PackageDownload |
| package_downloads |
| :package_id :id |
| :view :string |
| :downloads :integer |
>]
"Hexpm.Repository.PackageMetadata" [label= <| Hexpm.Repository.PackageMetadata |
| :id :binary_id |
| :description :string |
| :licenses {:array, :string} |
| :links {:map, :string} |
| :maintainers {:array, :string} |
| :extra :map |
>]
"Hexpm.Repository.PackageOwner" [label= <| Hexpm.Repository.PackageOwner |
| package_owners |
| :id :id |
| :level :string |
| :package_id :id |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.PackageReport" [label= <| Hexpm.Repository.PackageReport |
| package_reports |
| :id :id |
| :state :string |
| :description :string |
| :author_id :id |
| :package_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.PackageReportComment" [label= <| Hexpm.Repository.PackageReportComment |
| package_report_comments |
| :id :id |
| :text :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :package_report_id :id |
| :author_id :id |
>]
"Hexpm.Repository.PackageReportRelease" [label= <| Hexpm.Repository.PackageReportRelease |
| package_report_releases |
| :id :id |
| :release_id :id |
| :package_report_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.Release" [label= <| Hexpm.Repository.Release |
| releases |
| :id :id |
| :version Hexpm.Version |
| :inner_checksum :binary |
| :outer_checksum :binary |
| :has_docs :boolean |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :package_id :id |
| :publisher_id :id |
| :meta #Ecto.Embedded<[one: Hexpm.Repository.ReleaseMetadata]> |
| :retirement #Ecto.Embedded<[one: Hexpm.Repository.ReleaseRetirement]> |
>]
"Hexpm.Repository.ReleaseDownload" [label= <| Hexpm.Repository.ReleaseDownload |
| release_downloads |
| :package_id :id |
| :release_id :id |
| :downloads :integer |
>]
"Hexpm.Repository.ReleaseMetadata" [label= <| Hexpm.Repository.ReleaseMetadata |
| :id :binary_id |
| :app :string |
| :build_tools {:array, :string} |
| :elixir :string |
>]
"Hexpm.Repository.ReleaseRetirement" [label= <| Hexpm.Repository.ReleaseRetirement |
| :id :binary_id |
| :reason :string |
| :message :string |
>]
"Hexpm.Repository.Repository" [label= <| Hexpm.Repository.Repository |
| repositories |
| :id :id |
| :name :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :organization_id :id |
>]
"Hexpm.Repository.Requirement" [label= <| Hexpm.Repository.Requirement |
| requirements |
| :id :id |
| :app :string |
| :requirement :string |
| :optional :boolean |
| :release_id :id |
| :dependency_id :id |
>]
}
subgraph "cluster_Hexpm.ShortURLs" {
style=filled
fontname="Roboto Mono"
color = "#b4eeb4"
label = <Hexpm.ShortURLs>
"Hexpm.ShortURLs.ShortURL" [label= <| Hexpm.ShortURLs.ShortURL |
| short_urls |
| :id :id |
| :url :string |
| :short_code :string |
| :inserted_at :utc_datetime_usec |
>]
}
"Hexpm.Accounts.TFA":"field@recovery_codes":e -> "Hexpm.Accounts.RecoveryCode":"header@schema_module":w
"Hexpm.Accounts.Key":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@key_id":w
"Hexpm.Accounts.Key":"field@last_use":e -> "Hexpm.Accounts.Key.Use":"header@schema_module":w [dir=none]
"Hexpm.Accounts.Key":"field@permissions":e -> "Hexpm.Accounts.KeyPermission":"header@schema_module":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.Key":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Repository.Repository":"field@organization_id":w [dir=none]
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.User":"field@organization_id":w [dir=none]
"Hexpm.Repository.PackageReport":"field@id":e -> "Hexpm.Repository.PackageReportComment":"field@package_report_id":w
"Hexpm.Repository.PackageReport":"field@id":e -> "Hexpm.Repository.PackageReportRelease":"field@package_report_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Download":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageDependant":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageDownload":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageOwner":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageReport":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.ReleaseDownload":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Release":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Requirement":"field@dependency_id":w
"Hexpm.Repository.Package":"field@meta":e -> "Hexpm.Repository.PackageMetadata":"header@schema_module":w [dir=none]
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.Download":"field@release_id":w
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.PackageReportRelease":"field@release_id":w
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.ReleaseDownload":"field@release_id":w [dir=none]
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.Requirement":"field@release_id":w
"Hexpm.Repository.Release":"field@meta":e -> "Hexpm.Repository.ReleaseMetadata":"header@schema_module":w [dir=none]
"Hexpm.Repository.Release":"field@retirement":e -> "Hexpm.Repository.ReleaseRetirement":"header@schema_module":w [dir=none]
"Hexpm.Repository.Repository":"field@id":e -> "Hexpm.Repository.Package":"field@repository_id":w
"Hexpm.Accounts.User":"field@handles":e -> "Hexpm.Accounts.UserHandles":"header@schema_module":w [dir=none]
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Email":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Key":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageOwner":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageReportComment":"field@author_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageReport":"field@author_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.PasswordReset":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.Release":"field@publisher_id":w
"Hexpm.Accounts.User":"field@tfa":e -> "Hexpm.Accounts.TFA":"header@schema_module":w [dir=none]
}
================================================
FILE: examples/dot/hexpm/Default.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
"Hexpm.Accounts.AuditLog" [label= <| Hexpm.Accounts.AuditLog |
| audit_logs |
| :id :id |
| :user_agent :string |
| :remote_ip :string |
| :action :string |
| :params :map |
| :user_id :id |
| :organization_id :id |
| :key_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Email" [label= <| Hexpm.Accounts.Email |
| emails |
| :id :id |
| :email :string |
| :verified :boolean |
| :primary :boolean |
| :public :boolean |
| :gravatar :boolean |
| :verification_key :string |
| :verification_expiry :utc_datetime_usec |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Key" [label= <| Hexpm.Accounts.Key |
| keys |
| :id :id |
| :name :string |
| :secret_first :string |
| :secret_second :string |
| :public :boolean |
| :revoke_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :last_use #Ecto.Embedded<[one: Hexpm.Accounts.Key.Use]> |
| :user_id :id |
| :organization_id :id |
| :permissions #Ecto.Embedded<[many: Hexpm.Accounts.KeyPermission]> |
>]
"Hexpm.Accounts.Key.Use" [label= <| Hexpm.Accounts.Key.Use |
| :id :binary_id |
| :used_at :utc_datetime_usec |
| :user_agent :string |
| :ip :string |
>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
| :id :binary_id |
| :domain :string |
| :resource :string |
>]
"Hexpm.Accounts.Organization" [label= <| Hexpm.Accounts.Organization |
| organizations |
| :id :id |
| :name :string |
| :billing_active :boolean |
| :billing_override :boolean |
| :trial_end :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.OrganizationUser" [label= <| Hexpm.Accounts.OrganizationUser |
| organization_users |
| :id :id |
| :role :string |
| :organization_id :id |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.PasswordReset" [label= <| Hexpm.Accounts.PasswordReset |
| password_resets |
| :id :id |
| :key :string |
| :primary_email :string |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
| :id :binary_id |
| :code :string |
| :used_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Session" [label= <| Hexpm.Accounts.Session |
| sessions |
| :id :id |
| :token :binary |
| :data :map |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.TFA" [label= <| Hexpm.Accounts.TFA |
| :secret :string |
| :tfa_enabled :boolean |
| :app_enabled :boolean |
| :recovery_codes #Ecto.Embedded<[many: Hexpm.Accounts.RecoveryCode]> |
>]
"Hexpm.Accounts.User" [label= <| Hexpm.Accounts.User |
| users |
| :id :id |
| :username :string |
| :full_name :string |
| :password :string |
| :service :boolean |
| :deactivated_at :utc_datetime_usec |
| :role :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :handles #Ecto.Embedded<[one: Hexpm.Accounts.UserHandles]> |
| :tfa #Ecto.Embedded<[one: Hexpm.Accounts.TFA]> |
| :organization_id :id |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
| :id :binary_id |
| :twitter :string |
| :github :string |
| :elixirforum :string |
| :freenode :string |
| :slack :string |
>]
"Hexpm.BlockAddress.Entry" [label= <| Hexpm.BlockAddress.Entry |
| blocked_addresses |
| :id :id |
| :ip :string |
| :comment :string |
>]
"Hexpm.Repository.Download" [label= <| Hexpm.Repository.Download |
| downloads |
| :id :id |
| :package_id :id |
| :release_id :id |
| :downloads :integer |
| :day :date |
>]
"Hexpm.Repository.Install" [label= <| Hexpm.Repository.Install |
| installs |
| :id :id |
| :hex :string |
| :elixirs {:array, :string} |
>]
"Hexpm.Repository.Package" [label= <| Hexpm.Repository.Package |
| packages |
| :id :id |
| :name :string |
| :docs_updated_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :repository_id :id |
| :meta #Ecto.Embedded<[one: Hexpm.Repository.PackageMetadata]> |
>]
"Hexpm.Repository.PackageDependant" [label= <| Hexpm.Repository.PackageDependant |
| package_dependants |
| :id :id |
| :package_id :id |
| :name :string |
| :repo :string |
>]
"Hexpm.Repository.PackageDownload" [label= <| Hexpm.Repository.PackageDownload |
| package_downloads |
| :package_id :id |
| :view :string |
| :downloads :integer |
>]
"Hexpm.Repository.PackageMetadata" [label= <| Hexpm.Repository.PackageMetadata |
| :id :binary_id |
| :description :string |
| :licenses {:array, :string} |
| :links {:map, :string} |
| :maintainers {:array, :string} |
| :extra :map |
>]
"Hexpm.Repository.PackageOwner" [label= <| Hexpm.Repository.PackageOwner |
| package_owners |
| :id :id |
| :level :string |
| :package_id :id |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.PackageReport" [label= <| Hexpm.Repository.PackageReport |
| package_reports |
| :id :id |
| :state :string |
| :description :string |
| :author_id :id |
| :package_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.PackageReportComment" [label= <| Hexpm.Repository.PackageReportComment |
| package_report_comments |
| :id :id |
| :text :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :package_report_id :id |
| :author_id :id |
>]
"Hexpm.Repository.PackageReportRelease" [label= <| Hexpm.Repository.PackageReportRelease |
| package_report_releases |
| :id :id |
| :release_id :id |
| :package_report_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Repository.Release" [label= <| Hexpm.Repository.Release |
| releases |
| :id :id |
| :version Hexpm.Version |
| :inner_checksum :binary |
| :outer_checksum :binary |
| :has_docs :boolean |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :package_id :id |
| :publisher_id :id |
| :meta #Ecto.Embedded<[one: Hexpm.Repository.ReleaseMetadata]> |
| :retirement #Ecto.Embedded<[one: Hexpm.Repository.ReleaseRetirement]> |
>]
"Hexpm.Repository.ReleaseDownload" [label= <| Hexpm.Repository.ReleaseDownload |
| release_downloads |
| :package_id :id |
| :release_id :id |
| :downloads :integer |
>]
"Hexpm.Repository.ReleaseMetadata" [label= <| Hexpm.Repository.ReleaseMetadata |
| :id :binary_id |
| :app :string |
| :build_tools {:array, :string} |
| :elixir :string |
>]
"Hexpm.Repository.ReleaseRetirement" [label= <| Hexpm.Repository.ReleaseRetirement |
| :id :binary_id |
| :reason :string |
| :message :string |
>]
"Hexpm.Repository.Repository" [label= <| Hexpm.Repository.Repository |
| repositories |
| :id :id |
| :name :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :organization_id :id |
>]
"Hexpm.Repository.Requirement" [label= <| Hexpm.Repository.Requirement |
| requirements |
| :id :id |
| :app :string |
| :requirement :string |
| :optional :boolean |
| :release_id :id |
| :dependency_id :id |
>]
"Hexpm.ShortURLs.ShortURL" [label= <| Hexpm.ShortURLs.ShortURL |
| short_urls |
| :id :id |
| :url :string |
| :short_code :string |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.TFA":"field@recovery_codes":e -> "Hexpm.Accounts.RecoveryCode":"header@schema_module":w
"Hexpm.Accounts.Key":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@key_id":w
"Hexpm.Accounts.Key":"field@last_use":e -> "Hexpm.Accounts.Key.Use":"header@schema_module":w [dir=none]
"Hexpm.Accounts.Key":"field@permissions":e -> "Hexpm.Accounts.KeyPermission":"header@schema_module":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.Key":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Repository.Repository":"field@organization_id":w [dir=none]
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.User":"field@organization_id":w [dir=none]
"Hexpm.Repository.PackageReport":"field@id":e -> "Hexpm.Repository.PackageReportComment":"field@package_report_id":w
"Hexpm.Repository.PackageReport":"field@id":e -> "Hexpm.Repository.PackageReportRelease":"field@package_report_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Download":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageDependant":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageDownload":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageOwner":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.PackageReport":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.ReleaseDownload":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Release":"field@package_id":w
"Hexpm.Repository.Package":"field@id":e -> "Hexpm.Repository.Requirement":"field@dependency_id":w
"Hexpm.Repository.Package":"field@meta":e -> "Hexpm.Repository.PackageMetadata":"header@schema_module":w [dir=none]
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.Download":"field@release_id":w
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.PackageReportRelease":"field@release_id":w
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.ReleaseDownload":"field@release_id":w [dir=none]
"Hexpm.Repository.Release":"field@id":e -> "Hexpm.Repository.Requirement":"field@release_id":w
"Hexpm.Repository.Release":"field@meta":e -> "Hexpm.Repository.ReleaseMetadata":"header@schema_module":w [dir=none]
"Hexpm.Repository.Release":"field@retirement":e -> "Hexpm.Repository.ReleaseRetirement":"header@schema_module":w [dir=none]
"Hexpm.Repository.Repository":"field@id":e -> "Hexpm.Repository.Package":"field@repository_id":w
"Hexpm.Accounts.User":"field@handles":e -> "Hexpm.Accounts.UserHandles":"header@schema_module":w [dir=none]
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Email":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Key":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageOwner":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageReportComment":"field@author_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.PackageReport":"field@author_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.PasswordReset":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Repository.Release":"field@publisher_id":w
"Hexpm.Accounts.User":"field@tfa":e -> "Hexpm.Accounts.TFA":"header@schema_module":w [dir=none]
}
================================================
FILE: examples/dot/hexpm/No-fields.dot
================================================
strict digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
>]
"Hexpm.Accounts.AuditLog" [label= <| Hexpm.Accounts.AuditLog |
| audit_logs |
>]
"Hexpm.Accounts.Email" [label= <| Hexpm.Accounts.Email |
| emails |
>]
"Hexpm.Accounts.Key" [label= <>]
"Hexpm.Accounts.Key.Use" [label= <>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
>]
"Hexpm.Accounts.Organization" [label= <| Hexpm.Accounts.Organization |
| organizations |
>]
"Hexpm.Accounts.OrganizationUser" [label= <| Hexpm.Accounts.OrganizationUser |
| organization_users |
>]
"Hexpm.Accounts.PasswordReset" [label= <| Hexpm.Accounts.PasswordReset |
| password_resets |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
>]
"Hexpm.Accounts.Session" [label= <| Hexpm.Accounts.Session |
| sessions |
>]
"Hexpm.Accounts.TFA" [label= <>]
"Hexpm.Accounts.User" [label= <| Hexpm.Accounts.User |
| users |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
>]
"Hexpm.BlockAddress.Entry" [label= <| Hexpm.BlockAddress.Entry |
| blocked_addresses |
>]
"Hexpm.Repository.Download" [label= <| Hexpm.Repository.Download |
| downloads |
>]
"Hexpm.Repository.Install" [label= <| Hexpm.Repository.Install |
| installs |
>]
"Hexpm.Repository.Package" [label= <| Hexpm.Repository.Package |
| packages |
>]
"Hexpm.Repository.PackageDependant" [label= <| Hexpm.Repository.PackageDependant |
| package_dependants |
>]
"Hexpm.Repository.PackageDownload" [label= <| Hexpm.Repository.PackageDownload |
| package_downloads |
>]
"Hexpm.Repository.PackageMetadata" [label= <| Hexpm.Repository.PackageMetadata |
>]
"Hexpm.Repository.PackageOwner" [label= <| Hexpm.Repository.PackageOwner |
| package_owners |
>]
"Hexpm.Repository.PackageReport" [label= <| Hexpm.Repository.PackageReport |
| package_reports |
>]
"Hexpm.Repository.PackageReportComment" [label= <| Hexpm.Repository.PackageReportComment |
| package_report_comments |
>]
"Hexpm.Repository.PackageReportRelease" [label= <| Hexpm.Repository.PackageReportRelease |
| package_report_releases |
>]
"Hexpm.Repository.Release" [label= <| Hexpm.Repository.Release |
| releases |
>]
"Hexpm.Repository.ReleaseDownload" [label= <| Hexpm.Repository.ReleaseDownload |
| release_downloads |
>]
"Hexpm.Repository.ReleaseMetadata" [label= <| Hexpm.Repository.ReleaseMetadata |
>]
"Hexpm.Repository.ReleaseRetirement" [label= <| Hexpm.Repository.ReleaseRetirement |
>]
"Hexpm.Repository.Repository" [label= <| Hexpm.Repository.Repository |
| repositories |
>]
"Hexpm.Repository.Requirement" [label= <| Hexpm.Repository.Requirement |
| requirements |
>]
"Hexpm.ShortURLs.ShortURL" [label= <| Hexpm.ShortURLs.ShortURL |
| short_urls |
>]
"Hexpm.Accounts.TFA":e -> "Hexpm.Accounts.RecoveryCode":w
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.Key.Use":w [dir=none]
"Hexpm.Accounts.Key":e -> "Hexpm.Accounts.KeyPermission":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.Key":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.OrganizationUser":w
"Hexpm.Accounts.Organization":e -> "Hexpm.Repository.Repository":w [dir=none]
"Hexpm.Accounts.Organization":e -> "Hexpm.Accounts.User":w [dir=none]
"Hexpm.Repository.PackageReport":e -> "Hexpm.Repository.PackageReportComment":w
"Hexpm.Repository.PackageReport":e -> "Hexpm.Repository.PackageReportRelease":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Download":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageDependant":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageDownload":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageOwner":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageReport":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.ReleaseDownload":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Release":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.Requirement":w
"Hexpm.Repository.Package":e -> "Hexpm.Repository.PackageMetadata":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.Download":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.PackageReportRelease":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseDownload":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.Requirement":w
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseMetadata":w [dir=none]
"Hexpm.Repository.Release":e -> "Hexpm.Repository.ReleaseRetirement":w [dir=none]
"Hexpm.Repository.Repository":e -> "Hexpm.Repository.Package":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.UserHandles":w [dir=none]
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.AuditLog":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.Email":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.Key":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.OrganizationUser":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageOwner":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageReportComment":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.PackageReport":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.PasswordReset":w
"Hexpm.Accounts.User":e -> "Hexpm.Repository.Release":w
"Hexpm.Accounts.User":e -> "Hexpm.Accounts.TFA":w [dir=none]
}
================================================
FILE: examples/dot/hexpm/Only-embedded-schemas.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Hexpm.Accounts.Key.Use" [label= <| Hexpm.Accounts.Key.Use |
| :id :binary_id |
| :used_at :utc_datetime_usec |
| :user_agent :string |
| :ip :string |
>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
| :id :binary_id |
| :domain :string |
| :resource :string |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
| :id :binary_id |
| :code :string |
| :used_at :utc_datetime_usec |
>]
"Hexpm.Accounts.TFA" [label= <| Hexpm.Accounts.TFA |
| :secret :string |
| :tfa_enabled :boolean |
| :app_enabled :boolean |
| :recovery_codes #Ecto.Embedded<[many: Hexpm.Accounts.RecoveryCode]> |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
| :id :binary_id |
| :twitter :string |
| :github :string |
| :elixirforum :string |
| :freenode :string |
| :slack :string |
>]
"Hexpm.Repository.PackageMetadata" [label= <| Hexpm.Repository.PackageMetadata |
| :id :binary_id |
| :description :string |
| :licenses {:array, :string} |
| :links {:map, :string} |
| :maintainers {:array, :string} |
| :extra :map |
>]
"Hexpm.Repository.ReleaseMetadata" [label= <| Hexpm.Repository.ReleaseMetadata |
| :id :binary_id |
| :app :string |
| :build_tools {:array, :string} |
| :elixir :string |
>]
"Hexpm.Repository.ReleaseRetirement" [label= <| Hexpm.Repository.ReleaseRetirement |
| :id :binary_id |
| :reason :string |
| :message :string |
>]
"Hexpm.Accounts.TFA":"field@recovery_codes":e -> "Hexpm.Accounts.RecoveryCode":"header@schema_module":w
}
================================================
FILE: examples/dot/hexpm/Only-selected-cluster-Accounts-context.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
subgraph "cluster_Hexpm.Accounts" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Hexpm.Accounts>
"Hexpm.Accounts.AuditLog" [label= <| Hexpm.Accounts.AuditLog |
| audit_logs |
| :id :id |
| :user_agent :string |
| :remote_ip :string |
| :action :string |
| :params :map |
| :user_id :id |
| :organization_id :id |
| :key_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Email" [label= <| Hexpm.Accounts.Email |
| emails |
| :id :id |
| :email :string |
| :verified :boolean |
| :primary :boolean |
| :public :boolean |
| :gravatar :boolean |
| :verification_key :string |
| :verification_expiry :utc_datetime_usec |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Key" [label= <| Hexpm.Accounts.Key |
| keys |
| :id :id |
| :name :string |
| :secret_first :string |
| :secret_second :string |
| :public :boolean |
| :revoke_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :last_use #Ecto.Embedded<[one: Hexpm.Accounts.Key.Use]> |
| :user_id :id |
| :organization_id :id |
| :permissions #Ecto.Embedded<[many: Hexpm.Accounts.KeyPermission]> |
>]
"Hexpm.Accounts.Key.Use" [label= <| Hexpm.Accounts.Key.Use |
| :id :binary_id |
| :used_at :utc_datetime_usec |
| :user_agent :string |
| :ip :string |
>]
"Hexpm.Accounts.KeyPermission" [label= <| Hexpm.Accounts.KeyPermission |
| :id :binary_id |
| :domain :string |
| :resource :string |
>]
"Hexpm.Accounts.Organization" [label= <| Hexpm.Accounts.Organization |
| organizations |
| :id :id |
| :name :string |
| :billing_active :boolean |
| :billing_override :boolean |
| :trial_end :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.OrganizationUser" [label= <| Hexpm.Accounts.OrganizationUser |
| organization_users |
| :id :id |
| :role :string |
| :organization_id :id |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.PasswordReset" [label= <| Hexpm.Accounts.PasswordReset |
| password_resets |
| :id :id |
| :key :string |
| :primary_email :string |
| :user_id :id |
| :inserted_at :utc_datetime_usec |
>]
"Hexpm.Accounts.RecoveryCode" [label= <| Hexpm.Accounts.RecoveryCode |
| :id :binary_id |
| :code :string |
| :used_at :utc_datetime_usec |
>]
"Hexpm.Accounts.Session" [label= <| Hexpm.Accounts.Session |
| sessions |
| :id :id |
| :token :binary |
| :data :map |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
>]
"Hexpm.Accounts.TFA" [label= <| Hexpm.Accounts.TFA |
| :secret :string |
| :tfa_enabled :boolean |
| :app_enabled :boolean |
| :recovery_codes #Ecto.Embedded<[many: Hexpm.Accounts.RecoveryCode]> |
>]
"Hexpm.Accounts.User" [label= <| Hexpm.Accounts.User |
| users |
| :id :id |
| :username :string |
| :full_name :string |
| :password :string |
| :service :boolean |
| :deactivated_at :utc_datetime_usec |
| :role :string |
| :inserted_at :utc_datetime_usec |
| :updated_at :utc_datetime_usec |
| :handles #Ecto.Embedded<[one: Hexpm.Accounts.UserHandles]> |
| :tfa #Ecto.Embedded<[one: Hexpm.Accounts.TFA]> |
| :organization_id :id |
>]
"Hexpm.Accounts.UserHandles" [label= <| Hexpm.Accounts.UserHandles |
| :id :binary_id |
| :twitter :string |
| :github :string |
| :elixirforum :string |
| :freenode :string |
| :slack :string |
>]
}
"Hexpm.Accounts.TFA":"field@recovery_codes":e -> "Hexpm.Accounts.RecoveryCode":"header@schema_module":w
"Hexpm.Accounts.Key":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@key_id":w
"Hexpm.Accounts.Key":"field@last_use":e -> "Hexpm.Accounts.Key.Use":"header@schema_module":w [dir=none]
"Hexpm.Accounts.Key":"field@permissions":e -> "Hexpm.Accounts.KeyPermission":"header@schema_module":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.Key":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@organization_id":w
"Hexpm.Accounts.Organization":"field@id":e -> "Hexpm.Accounts.User":"field@organization_id":w [dir=none]
"Hexpm.Accounts.User":"field@handles":e -> "Hexpm.Accounts.UserHandles":"header@schema_module":w [dir=none]
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.AuditLog":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Email":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.Key":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.OrganizationUser":"field@user_id":w
"Hexpm.Accounts.User":"field@id":e -> "Hexpm.Accounts.PasswordReset":"field@user_id":w
"Hexpm.Accounts.User":"field@tfa":e -> "Hexpm.Accounts.TFA":"header@schema_module":w [dir=none]
}
================================================
FILE: examples/dot/plausible-analytics/Contexts-as-clusters-no-fields.dot
================================================
strict digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
subgraph "cluster_Ecto.Migration" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Ecto.Migration>
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
>]
}
subgraph "cluster_FunWithFlags.Store" {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <FunWithFlags.Store>
"FunWithFlags.Store.Persistent.Ecto.Record" [label= <| FunWithFlags.Store.Persistent.Ecto.Record |
| fun_with_flags_toggles |
>]
}
subgraph cluster_Oban {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <Oban>
"Oban.Job" [label= <>]
}
subgraph cluster_Plausible {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <Plausible>
"Plausible.ClickhouseEventV2" [label= <| Plausible.ClickhouseEventV2 |
| events_v2 |
>]
"Plausible.ClickhouseSessionV2" [label= <| Plausible.ClickhouseSessionV2 |
| sessions_v2 |
>]
"Plausible.Funnel" [label= <>]
"Plausible.Goal" [label= <>]
"Plausible.Site" [label= <>]
}
subgraph "cluster_Plausible.Auth" {
style=filled
fontname="Roboto Mono"
color = "#ffefd5"
label = <Plausible.Auth>
"Plausible.Auth.ApiKey" [label= <| Plausible.Auth.ApiKey |
| api_keys |
>]
"Plausible.Auth.EmailActivationCode" [label= <| Plausible.Auth.EmailActivationCode |
| email_activation_codes |
>]
"Plausible.Auth.GracePeriod" [label= <| Plausible.Auth.GracePeriod |
>]
"Plausible.Auth.Invitation" [label= <| Plausible.Auth.Invitation |
| invitations |
>]
"Plausible.Auth.TOTP.RecoveryCode" [label= <| Plausible.Auth.TOTP.RecoveryCode |
| totp_recovery_codes |
>]
"Plausible.Auth.User" [label= <| Plausible.Auth.User |
| users |
>]
}
subgraph "cluster_Plausible.Billing" {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <Plausible.Billing>
"Plausible.Billing.EnterprisePlan" [label= <| Plausible.Billing.EnterprisePlan |
| enterprise_plans |
>]
"Plausible.Billing.Plan" [label= <>]
"Plausible.Billing.Subscription" [label= <| Plausible.Billing.Subscription |
| subscriptions |
>]
}
subgraph "cluster_Plausible.DataMigration" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.DataMigration>
"Plausible.DataMigration.NumericIDs.DomainsLookup" [label= <| Plausible.DataMigration.NumericIDs.DomainsLookup |
| domains_lookup |
>]
}
subgraph "cluster_Plausible.Funnel" {
style=filled
fontname="Roboto Mono"
color = "#fffafa"
label = <Plausible.Funnel>
"Plausible.Funnel.Step" [label= <| Plausible.Funnel.Step |
| funnel_steps |
>]
}
subgraph "cluster_Plausible.Imported" {
style=filled
fontname="Roboto Mono"
color = "#eedfcc"
label = <Plausible.Imported>
"Plausible.Imported.Browser" [label= <| Plausible.Imported.Browser |
| imported_browsers |
>]
"Plausible.Imported.Device" [label= <| Plausible.Imported.Device |
| imported_devices |
>]
"Plausible.Imported.EntryPage" [label= <| Plausible.Imported.EntryPage |
| imported_entry_pages |
>]
"Plausible.Imported.ExitPage" [label= <| Plausible.Imported.ExitPage |
| imported_exit_pages |
>]
"Plausible.Imported.Location" [label= <| Plausible.Imported.Location |
| imported_locations |
>]
"Plausible.Imported.OperatingSystem" [label= <| Plausible.Imported.OperatingSystem |
| imported_operating_systems |
>]
"Plausible.Imported.Page" [label= <| Plausible.Imported.Page |
| imported_pages |
>]
"Plausible.Imported.SiteImport" [label= <| Plausible.Imported.SiteImport |
| site_imports |
>]
"Plausible.Imported.Source" [label= <| Plausible.Imported.Source |
| imported_sources |
>]
"Plausible.Imported.Visitor" [label= <| Plausible.Imported.Visitor |
| imported_visitors |
>]
}
subgraph "cluster_Plausible.Ingestion" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.Ingestion>
"Plausible.Ingestion.Counters.Record" [label= <| Plausible.Ingestion.Counters.Record |
| ingest_counters |
>]
"Plausible.Ingestion.Request" [label= <| Plausible.Ingestion.Request |
>]
}
subgraph "cluster_Plausible.Plugins" {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <Plausible.Plugins>
"Plausible.Plugins.API.Token" [label= <| Plausible.Plugins.API.Token |
| plugins_api_tokens |
>]
}
subgraph "cluster_Plausible.Shield" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.Shield>
"Plausible.Shield.CountryRule" [label= <| Plausible.Shield.CountryRule |
| shield_rules_country |
>]
"Plausible.Shield.HostnameRule" [label= <| Plausible.Shield.HostnameRule |
| shield_rules_hostname |
>]
"Plausible.Shield.IPRule" [label= <| Plausible.Shield.IPRule |
| shield_rules_ip |
>]
"Plausible.Shield.PageRule" [label= <| Plausible.Shield.PageRule |
| shield_rules_page |
>]
}
subgraph "cluster_Plausible.Site" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Plausible.Site>
"Plausible.Site.GoogleAuth" [label= <| Plausible.Site.GoogleAuth |
| google_auth |
>]
"Plausible.Site.ImportedData" [label= <| Plausible.Site.ImportedData |
>]
"Plausible.Site.Membership" [label= <| Plausible.Site.Membership |
| site_memberships |
>]
"Plausible.Site.MonthlyReport" [label= <| Plausible.Site.MonthlyReport |
| monthly_reports |
>]
"Plausible.Site.SharedLink" [label= <| Plausible.Site.SharedLink |
| shared_links |
>]
"Plausible.Site.SpikeNotification" [label= <| Plausible.Site.SpikeNotification |
| spike_notifications |
>]
"Plausible.Site.UserPreference" [label= <| Plausible.Site.UserPreference |
| site_user_preferences |
>]
"Plausible.Site.WeeklyReport" [label= <| Plausible.Site.WeeklyReport |
| weekly_reports |
>]
}
"Plausible.Funnel":e -> "Plausible.Funnel.Step":w
"Plausible.Goal":e -> "Plausible.Funnel.Step":w
"Plausible.Site":e -> "Plausible.Funnel":w
"Plausible.Site":e -> "Plausible.Goal":w
"Plausible.Site":e -> "Plausible.Site.GoogleAuth":w [dir=none]
"Plausible.Site":e -> "Plausible.Auth.Invitation":w
"Plausible.Site":e -> "Plausible.Site.MonthlyReport":w [dir=none]
"Plausible.Site":e -> "Plausible.Plugins.API.Token":w
"Plausible.Site":e -> "Plausible.Site.SharedLink":w
"Plausible.Site":e -> "Plausible.Shield.CountryRule":w
"Plausible.Site":e -> "Plausible.Shield.HostnameRule":w
"Plausible.Site":e -> "Plausible.Shield.IPRule":w
"Plausible.Site":e -> "Plausible.Shield.PageRule":w
"Plausible.Site":e -> "Plausible.Imported.SiteImport":w
"Plausible.Site":e -> "Plausible.Site.Membership":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.UserPreference":w
"Plausible.Site":e -> "Plausible.Site.SpikeNotification":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.WeeklyReport":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.ImportedData":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.GracePeriod":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.ApiKey":w
"Plausible.Auth.User":e -> "Plausible.Auth.EmailActivationCode":w
"Plausible.Auth.User":e -> "Plausible.Billing.EnterprisePlan":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Site.GoogleAuth":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.Invitation":w
"Plausible.Auth.User":e -> "Plausible.Imported.SiteImport":w
"Plausible.Auth.User":e -> "Plausible.Site.Membership":w
"Plausible.Auth.User":e -> "Plausible.Site.UserPreference":w
"Plausible.Auth.User":e -> "Plausible.Billing.Subscription":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.TOTP.RecoveryCode":w
}
================================================
FILE: examples/dot/plausible-analytics/Contexts-as-clusters.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
subgraph "cluster_Ecto.Migration" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Ecto.Migration>
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
}
subgraph "cluster_FunWithFlags.Store" {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <FunWithFlags.Store>
"FunWithFlags.Store.Persistent.Ecto.Record" [label= <| FunWithFlags.Store.Persistent.Ecto.Record |
| fun_with_flags_toggles |
| :id :id |
| :flag_name :string |
| :gate_type :string |
| :target :string |
| :enabled :boolean |
>]
}
subgraph cluster_Oban {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <Oban>
"Oban.Job" [label= <| Oban.Job |
| oban_jobs |
| :id :id |
| :state :string |
| :queue :string |
| :worker :string |
| :args :map |
| :meta :map |
| :tags {:array, :string} |
| :errors {:array, :map} |
| :attempt :integer |
| :attempted_by {:array, :string} |
| :max_attempts :integer |
| :priority :integer |
| :attempted_at :utc_datetime_usec |
| :cancelled_at :utc_datetime_usec |
| :completed_at :utc_datetime_usec |
| :discarded_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :scheduled_at :utc_datetime_usec |
>]
}
subgraph cluster_Plausible {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <Plausible>
"Plausible.ClickhouseEventV2" [label= <| Plausible.ClickhouseEventV2 |
| events_v2 |
| :name {:parameterized, Ch, {:low_cardinality, :string}} |
| :site_id {:parameterized, Ch, :u64} |
| :hostname :string |
| :pathname :string |
| :user_id {:parameterized, Ch, :u64} |
| :session_id {:parameterized, Ch, :u64} |
| :timestamp :naive_datetime |
| :"meta.key" {:array, :string} |
| :"meta.value" {:array, :string} |
| :revenue_source_amount {:parameterized, Ch, {:nullable, {:decimal64, 3}}} |
| :revenue_source_currency {:parameterized, Ch, {:fixed_string, 3}} |
| :revenue_reporting_amount {:parameterized, Ch, {:nullable, {:decimal64, 3}}} |
| :revenue_reporting_currency {:parameterized, Ch, {:fixed_string, 3}} |
| :referrer :string |
| :referrer_source :string |
| :utm_medium :string |
| :utm_source :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :country_code {:parameterized, Ch, {:fixed_string, 2}} |
| :subdivision1_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :subdivision2_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :city_geoname_id {:parameterized, Ch, :u32} |
| :screen_size {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser_version {:parameterized, Ch, {:low_cardinality, :string}} |
>]
"Plausible.ClickhouseSessionV2" [label= <| Plausible.ClickhouseSessionV2 |
| sessions_v2 |
| :hostname :string |
| :site_id {:parameterized, Ch, :u64} |
| :user_id {:parameterized, Ch, :u64} |
| :session_id {:parameterized, Ch, :u64} |
| :start :naive_datetime |
| :duration {:parameterized, Ch, :u32} |
| :is_bounce Plausible.ClickhouseSessionV2.BoolUInt8 |
| :entry_page :string |
| :exit_page :string |
| :exit_page_hostname :string |
| :pageviews {:parameterized, Ch, :i32} |
| :events {:parameterized, Ch, :i32} |
| :sign {:parameterized, Ch, :i8} |
| :"entry_meta.key" {:array, :string} |
| :"entry_meta.value" {:array, :string} |
| :utm_medium :string |
| :utm_source :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :referrer :string |
| :referrer_source :string |
| :country_code {:parameterized, Ch, {:low_cardinality, {:fixed_string, 2}}} |
| :subdivision1_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :subdivision2_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :city_geoname_id {:parameterized, Ch, :u32} |
| :screen_size {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :timestamp :naive_datetime |
| :transferred_from :string |
>]
"Plausible.Funnel" [label= <| Plausible.Funnel |
| funnels |
| :id :id |
| :name :string |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Goal" [label= <| Plausible.Goal |
| goals |
| :id :id |
| :event_name :string |
| :page_path :string |
| :currency #Enum<[:AED, :AFN, :ALL, :AMD, :ANG, :AOA, :ARS, :AUD, :AWG, :AZN, ...]> |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site" [label= <| Plausible.Site |
| sites |
| :id :id |
| :domain :string |
| :timezone :string |
| :public :boolean |
| :locked :boolean |
| :stats_start_date :date |
| :native_stats_start_at :naive_datetime |
| :allowed_event_props {:array, :string} |
| :conversions_enabled :boolean |
| :props_enabled :boolean |
| :funnels_enabled :boolean |
| :ingest_rate_limit_scale_seconds :integer |
| :ingest_rate_limit_threshold :integer |
| :domain_changed_from :string |
| :domain_changed_at :naive_datetime |
| :imported_data #Ecto.Embedded<[one: Plausible.Site.ImportedData]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph "cluster_Plausible.Auth" {
style=filled
fontname="Roboto Mono"
color = "#ffefd5"
label = <Plausible.Auth>
"Plausible.Auth.ApiKey" [label= <| Plausible.Auth.ApiKey |
| api_keys |
| :id :id |
| :name :string |
| :scopes {:array, :string} |
| :hourly_request_limit :integer |
| :key_hash :string |
| :key_prefix :string |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Auth.EmailActivationCode" [label= <| Plausible.Auth.EmailActivationCode |
| email_activation_codes |
| :id :id |
| :code :string |
| :issued_at :naive_datetime |
| :user_id :id |
>]
"Plausible.Auth.GracePeriod" [label= <| Plausible.Auth.GracePeriod |
| :id :binary_id |
| :end_date :date |
| :is_over :boolean |
| :manual_lock :boolean |
>]
"Plausible.Auth.Invitation" [label= <| Plausible.Auth.Invitation |
| invitations |
| :id :id |
| :invitation_id :string |
| :email :string |
| :role #Enum<[:admin, :owner, :viewer]> |
| :inviter_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Auth.TOTP.RecoveryCode" [label= <| Plausible.Auth.TOTP.RecoveryCode |
| totp_recovery_codes |
| :id :id |
| :code_digest :string |
| :user_id :id |
| :inserted_at :naive_datetime |
>]
"Plausible.Auth.User" [label= <| Plausible.Auth.User |
| users |
| :id :id |
| :email :string |
| :password_hash :string |
| :name :string |
| :last_seen :naive_datetime |
| :trial_expiry_date :date |
| :theme #Enum<[:dark, :light, :system]> |
| :email_verified :boolean |
| :previous_email :string |
| :accept_traffic_until :date |
| :allow_next_upgrade_override :boolean |
| :totp_enabled :boolean |
| :totp_secret Plausible.Auth.TOTP.EncryptedBinary |
| :totp_token :string |
| :totp_last_used_at :naive_datetime |
| :grace_period #Ecto.Embedded<[one: Plausible.Auth.GracePeriod]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph "cluster_Plausible.Billing" {
style=filled
fontname="Roboto Mono"
color = "#f0ffff"
label = <Plausible.Billing>
"Plausible.Billing.EnterprisePlan" [label= <| Plausible.Billing.EnterprisePlan |
| enterprise_plans |
| :id :id |
| :paddle_plan_id :string |
| :billing_interval #Enum<[:monthly, :yearly]> |
| :monthly_pageview_limit :integer |
| :site_limit :integer |
| :team_member_limit Plausible.Billing.Ecto.Limit |
| :features Plausible.Billing.Ecto.FeatureList |
| :hourly_api_request_limit :integer |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Billing.Plan" [label= <| Plausible.Billing.Plan |
| :id :binary_id |
| :generation :integer |
| :kind #Enum<[:business, :growth]> |
| :features Plausible.Billing.Ecto.FeatureList |
| :monthly_pageview_limit :integer |
| :site_limit :integer |
| :team_member_limit Plausible.Billing.Ecto.Limit |
| :volume :string |
| :data_retention_in_years :integer |
| :monthly_cost :string |
| :monthly_product_id :string |
| :yearly_cost :string |
| :yearly_product_id :string |
>]
"Plausible.Billing.Subscription" [label= <| Plausible.Billing.Subscription |
| subscriptions |
| :id :id |
| :paddle_subscription_id :string |
| :paddle_plan_id :string |
| :update_url :string |
| :cancel_url :string |
| :status #Enum<[:active, :deleted, :past_due, :paused]> |
| :next_bill_amount :string |
| :next_bill_date :date |
| :last_bill_date :date |
| :currency_code :string |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph "cluster_Plausible.DataMigration" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.DataMigration>
"Plausible.DataMigration.NumericIDs.DomainsLookup" [label= <| Plausible.DataMigration.NumericIDs.DomainsLookup |
| domains_lookup |
| :site_id {:parameterized, Ch, :u64} |
| :domain :string |
>]
}
subgraph "cluster_Plausible.Funnel" {
style=filled
fontname="Roboto Mono"
color = "#fffafa"
label = <Plausible.Funnel>
"Plausible.Funnel.Step" [label= <| Plausible.Funnel.Step |
| funnel_steps |
| :id :id |
| :step_order :integer |
| :funnel_id :id |
| :goal_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph "cluster_Plausible.Imported" {
style=filled
fontname="Roboto Mono"
color = "#eedfcc"
label = <Plausible.Imported>
"Plausible.Imported.Browser" [label= <| Plausible.Imported.Browser |
| imported_browsers |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :browser :string |
| :browser_version :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Device" [label= <| Plausible.Imported.Device |
| imported_devices |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :device :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.EntryPage" [label= <| Plausible.Imported.EntryPage |
| imported_entry_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :entry_page :string |
| :visitors {:parameterized, Ch, :u64} |
| :entrances {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.ExitPage" [label= <| Plausible.Imported.ExitPage |
| imported_exit_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :exit_page :string |
| :exits {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Location" [label= <| Plausible.Imported.Location |
| imported_locations |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :country :string |
| :region :string |
| :city {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.OperatingSystem" [label= <| Plausible.Imported.OperatingSystem |
| imported_operating_systems |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :operating_system :string |
| :operating_system_version :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Page" [label= <| Plausible.Imported.Page |
| imported_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :hostname :string |
| :page :string |
| :visits {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :active_visitors {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :exits {:parameterized, Ch, :u64} |
| :time_on_page {:parameterized, Ch, :u64} |
>]
"Plausible.Imported.SiteImport" [label= <| Plausible.Imported.SiteImport |
| site_imports |
| :id :id |
| :start_date :date |
| :end_date :date |
| :label :string |
| :source #Enum<[:csv, :google_analytics_4, :noop, :universal_analytics]> |
| :status #Enum<[:completed, :failed, :importing, :pending]> |
| :legacy :boolean |
| :site_id :id |
| :imported_by_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Imported.Source" [label= <| Plausible.Imported.Source |
| imported_sources |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :source :string |
| :referrer :string |
| :utm_source :string |
| :utm_medium :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Visitor" [label= <| Plausible.Imported.Visitor |
| imported_visitors |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :visitors {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
>]
}
subgraph "cluster_Plausible.Ingestion" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.Ingestion>
"Plausible.Ingestion.Counters.Record" [label= <| Plausible.Ingestion.Counters.Record |
| ingest_counters |
| :event_timebucket :utc_datetime |
| :site_id {:parameterized, Ch, {:nullable, :u64}} |
| :domain {:parameterized, Ch, {:low_cardinality, :string}} |
| :metric {:parameterized, Ch, {:low_cardinality, :string}} |
| :value {:parameterized, Ch, :u64} |
>]
"Plausible.Ingestion.Request" [label= <| Plausible.Ingestion.Request |
| :remote_ip :string |
| :user_agent :string |
| :event_name Plausible.Ecto.EventName |
| :uri :map |
| :hostname :string |
| :referrer :string |
| :domains {:array, :string} |
| :ip_classification :string |
| :hash_mode :integer |
| :pathname :string |
| :props :map |
| :revenue_source :map |
| :query_params :map |
| :timestamp :naive_datetime |
>]
}
subgraph "cluster_Plausible.Plugins" {
style=filled
fontname="Roboto Mono"
color = "#eee5de"
label = <Plausible.Plugins>
"Plausible.Plugins.API.Token" [label= <| Plausible.Plugins.API.Token |
| plugins_api_tokens |
| :id :binary_id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
| :token_hash :binary |
| :description :string |
| :hint :string |
| :last_used_at :naive_datetime |
| :site_id :id |
>]
}
subgraph "cluster_Plausible.Shield" {
style=filled
fontname="Roboto Mono"
color = "#8deeee"
label = <Plausible.Shield>
"Plausible.Shield.CountryRule" [label= <| Plausible.Shield.CountryRule |
| shield_rules_country |
| :id :binary_id |
| :site_id :id |
| :country_code :string |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.HostnameRule" [label= <| Plausible.Shield.HostnameRule |
| shield_rules_hostname |
| :id :binary_id |
| :site_id :id |
| :hostname :string |
| :hostname_pattern Plausible.Ecto.Types.CompiledRegex |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.IPRule" [label= <| Plausible.Shield.IPRule |
| shield_rules_ip |
| :id :binary_id |
| :site_id :id |
| :inet EctoNetwork.INET |
| :action #Enum<[:allow, :deny]> |
| :description :string |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.PageRule" [label= <| Plausible.Shield.PageRule |
| shield_rules_page |
| :id :binary_id |
| :site_id :id |
| :page_path :string |
| :page_path_pattern Plausible.Ecto.Types.CompiledRegex |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
subgraph "cluster_Plausible.Site" {
style=filled
fontname="Roboto Mono"
color = "#f0f8ff"
label = <Plausible.Site>
"Plausible.Site.GoogleAuth" [label= <| Plausible.Site.GoogleAuth |
| google_auth |
| :id :id |
| :email :string |
| :property :string |
| :refresh_token :string |
| :access_token :string |
| :expires :naive_datetime |
| :user_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.ImportedData" [label= <| Plausible.Site.ImportedData |
| :id :binary_id |
| :start_date :date |
| :end_date :date |
| :source :string |
| :status :string |
>]
"Plausible.Site.Membership" [label= <| Plausible.Site.Membership |
| site_memberships |
| :id :id |
| :role #Enum<[:admin, :owner, :viewer]> |
| :site_id :id |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.MonthlyReport" [label= <| Plausible.Site.MonthlyReport |
| monthly_reports |
| :id :id |
| :recipients {:array, :string} |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.SharedLink" [label= <| Plausible.Site.SharedLink |
| shared_links |
| :id :id |
| :site_id :id |
| :name :string |
| :slug :string |
| :password_hash :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.SpikeNotification" [label= <| Plausible.Site.SpikeNotification |
| spike_notifications |
| :id :id |
| :recipients {:array, :string} |
| :threshold :integer |
| :last_sent :naive_datetime |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.UserPreference" [label= <| Plausible.Site.UserPreference |
| site_user_preferences |
| :id :id |
| :pinned_at :naive_datetime |
| :user_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.WeeklyReport" [label= <| Plausible.Site.WeeklyReport |
| weekly_reports |
| :id :id |
| :recipients {:array, :string} |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
}
"Plausible.Funnel":"field@id":e -> "Plausible.Funnel.Step":"field@funnel_id":w
"Plausible.Goal":"field@id":e -> "Plausible.Funnel.Step":"field@goal_id":w
"Plausible.Site":"field@id":e -> "Plausible.Funnel":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Goal":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.GoogleAuth":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Auth.Invitation":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.MonthlyReport":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Plugins.API.Token":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.SharedLink":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.CountryRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.HostnameRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.IPRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.PageRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Imported.SiteImport":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.Membership":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Site.UserPreference":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.SpikeNotification":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Site.WeeklyReport":"field@site_id":w [dir=none]
"Plausible.Site":"field@imported_data":e -> "Plausible.Site.ImportedData":"header@schema_module":w [dir=none]
"Plausible.Auth.User":"field@grace_period":e -> "Plausible.Auth.GracePeriod":"header@schema_module":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.ApiKey":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.EmailActivationCode":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Billing.EnterprisePlan":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.GoogleAuth":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.Invitation":"field@inviter_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Imported.SiteImport":"field@imported_by_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.Membership":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.UserPreference":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Billing.Subscription":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.TOTP.RecoveryCode":"field@user_id":w
}
================================================
FILE: examples/dot/plausible-analytics/Default.dot
================================================
digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
| :version :integer |
| :inserted_at :naive_datetime |
>]
"FunWithFlags.Store.Persistent.Ecto.Record" [label= <| FunWithFlags.Store.Persistent.Ecto.Record |
| fun_with_flags_toggles |
| :id :id |
| :flag_name :string |
| :gate_type :string |
| :target :string |
| :enabled :boolean |
>]
"Oban.Job" [label= <| Oban.Job |
| oban_jobs |
| :id :id |
| :state :string |
| :queue :string |
| :worker :string |
| :args :map |
| :meta :map |
| :tags {:array, :string} |
| :errors {:array, :map} |
| :attempt :integer |
| :attempted_by {:array, :string} |
| :max_attempts :integer |
| :priority :integer |
| :attempted_at :utc_datetime_usec |
| :cancelled_at :utc_datetime_usec |
| :completed_at :utc_datetime_usec |
| :discarded_at :utc_datetime_usec |
| :inserted_at :utc_datetime_usec |
| :scheduled_at :utc_datetime_usec |
>]
"Plausible.Auth.ApiKey" [label= <| Plausible.Auth.ApiKey |
| api_keys |
| :id :id |
| :name :string |
| :scopes {:array, :string} |
| :hourly_request_limit :integer |
| :key_hash :string |
| :key_prefix :string |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Auth.EmailActivationCode" [label= <| Plausible.Auth.EmailActivationCode |
| email_activation_codes |
| :id :id |
| :code :string |
| :issued_at :naive_datetime |
| :user_id :id |
>]
"Plausible.Auth.GracePeriod" [label= <| Plausible.Auth.GracePeriod |
| :id :binary_id |
| :end_date :date |
| :is_over :boolean |
| :manual_lock :boolean |
>]
"Plausible.Auth.Invitation" [label= <| Plausible.Auth.Invitation |
| invitations |
| :id :id |
| :invitation_id :string |
| :email :string |
| :role #Enum<[:admin, :owner, :viewer]> |
| :inviter_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Auth.TOTP.RecoveryCode" [label= <| Plausible.Auth.TOTP.RecoveryCode |
| totp_recovery_codes |
| :id :id |
| :code_digest :string |
| :user_id :id |
| :inserted_at :naive_datetime |
>]
"Plausible.Auth.User" [label= <| Plausible.Auth.User |
| users |
| :id :id |
| :email :string |
| :password_hash :string |
| :name :string |
| :last_seen :naive_datetime |
| :trial_expiry_date :date |
| :theme #Enum<[:dark, :light, :system]> |
| :email_verified :boolean |
| :previous_email :string |
| :accept_traffic_until :date |
| :allow_next_upgrade_override :boolean |
| :totp_enabled :boolean |
| :totp_secret Plausible.Auth.TOTP.EncryptedBinary |
| :totp_token :string |
| :totp_last_used_at :naive_datetime |
| :grace_period #Ecto.Embedded<[one: Plausible.Auth.GracePeriod]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Billing.EnterprisePlan" [label= <| Plausible.Billing.EnterprisePlan |
| enterprise_plans |
| :id :id |
| :paddle_plan_id :string |
| :billing_interval #Enum<[:monthly, :yearly]> |
| :monthly_pageview_limit :integer |
| :site_limit :integer |
| :team_member_limit Plausible.Billing.Ecto.Limit |
| :features Plausible.Billing.Ecto.FeatureList |
| :hourly_api_request_limit :integer |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Billing.Plan" [label= <| Plausible.Billing.Plan |
| :id :binary_id |
| :generation :integer |
| :kind #Enum<[:business, :growth]> |
| :features Plausible.Billing.Ecto.FeatureList |
| :monthly_pageview_limit :integer |
| :site_limit :integer |
| :team_member_limit Plausible.Billing.Ecto.Limit |
| :volume :string |
| :data_retention_in_years :integer |
| :monthly_cost :string |
| :monthly_product_id :string |
| :yearly_cost :string |
| :yearly_product_id :string |
>]
"Plausible.Billing.Subscription" [label= <| Plausible.Billing.Subscription |
| subscriptions |
| :id :id |
| :paddle_subscription_id :string |
| :paddle_plan_id :string |
| :update_url :string |
| :cancel_url :string |
| :status #Enum<[:active, :deleted, :past_due, :paused]> |
| :next_bill_amount :string |
| :next_bill_date :date |
| :last_bill_date :date |
| :currency_code :string |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.ClickhouseEventV2" [label= <| Plausible.ClickhouseEventV2 |
| events_v2 |
| :name {:parameterized, Ch, {:low_cardinality, :string}} |
| :site_id {:parameterized, Ch, :u64} |
| :hostname :string |
| :pathname :string |
| :user_id {:parameterized, Ch, :u64} |
| :session_id {:parameterized, Ch, :u64} |
| :timestamp :naive_datetime |
| :"meta.key" {:array, :string} |
| :"meta.value" {:array, :string} |
| :revenue_source_amount {:parameterized, Ch, {:nullable, {:decimal64, 3}}} |
| :revenue_source_currency {:parameterized, Ch, {:fixed_string, 3}} |
| :revenue_reporting_amount {:parameterized, Ch, {:nullable, {:decimal64, 3}}} |
| :revenue_reporting_currency {:parameterized, Ch, {:fixed_string, 3}} |
| :referrer :string |
| :referrer_source :string |
| :utm_medium :string |
| :utm_source :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :country_code {:parameterized, Ch, {:fixed_string, 2}} |
| :subdivision1_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :subdivision2_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :city_geoname_id {:parameterized, Ch, :u32} |
| :screen_size {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser_version {:parameterized, Ch, {:low_cardinality, :string}} |
>]
"Plausible.ClickhouseSessionV2" [label= <| Plausible.ClickhouseSessionV2 |
| sessions_v2 |
| :hostname :string |
| :site_id {:parameterized, Ch, :u64} |
| :user_id {:parameterized, Ch, :u64} |
| :session_id {:parameterized, Ch, :u64} |
| :start :naive_datetime |
| :duration {:parameterized, Ch, :u32} |
| :is_bounce Plausible.ClickhouseSessionV2.BoolUInt8 |
| :entry_page :string |
| :exit_page :string |
| :exit_page_hostname :string |
| :pageviews {:parameterized, Ch, :i32} |
| :events {:parameterized, Ch, :i32} |
| :sign {:parameterized, Ch, :i8} |
| :"entry_meta.key" {:array, :string} |
| :"entry_meta.value" {:array, :string} |
| :utm_medium :string |
| :utm_source :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :referrer :string |
| :referrer_source :string |
| :country_code {:parameterized, Ch, {:low_cardinality, {:fixed_string, 2}}} |
| :subdivision1_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :subdivision2_code {:parameterized, Ch, {:low_cardinality, :string}} |
| :city_geoname_id {:parameterized, Ch, :u32} |
| :screen_size {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system {:parameterized, Ch, {:low_cardinality, :string}} |
| :operating_system_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser {:parameterized, Ch, {:low_cardinality, :string}} |
| :browser_version {:parameterized, Ch, {:low_cardinality, :string}} |
| :timestamp :naive_datetime |
| :transferred_from :string |
>]
"Plausible.DataMigration.NumericIDs.DomainsLookup" [label= <| Plausible.DataMigration.NumericIDs.DomainsLookup |
| domains_lookup |
| :site_id {:parameterized, Ch, :u64} |
| :domain :string |
>]
"Plausible.Funnel" [label= <| Plausible.Funnel |
| funnels |
| :id :id |
| :name :string |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Funnel.Step" [label= <| Plausible.Funnel.Step |
| funnel_steps |
| :id :id |
| :step_order :integer |
| :funnel_id :id |
| :goal_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Goal" [label= <| Plausible.Goal |
| goals |
| :id :id |
| :event_name :string |
| :page_path :string |
| :currency #Enum<[:AED, :AFN, :ALL, :AMD, :ANG, :AOA, :ARS, :AUD, :AWG, :AZN, ...]> |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Imported.Browser" [label= <| Plausible.Imported.Browser |
| imported_browsers |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :browser :string |
| :browser_version :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Device" [label= <| Plausible.Imported.Device |
| imported_devices |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :device :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.EntryPage" [label= <| Plausible.Imported.EntryPage |
| imported_entry_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :entry_page :string |
| :visitors {:parameterized, Ch, :u64} |
| :entrances {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.ExitPage" [label= <| Plausible.Imported.ExitPage |
| imported_exit_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :exit_page :string |
| :exits {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Location" [label= <| Plausible.Imported.Location |
| imported_locations |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :country :string |
| :region :string |
| :city {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.OperatingSystem" [label= <| Plausible.Imported.OperatingSystem |
| imported_operating_systems |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :operating_system :string |
| :operating_system_version :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Page" [label= <| Plausible.Imported.Page |
| imported_pages |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :hostname :string |
| :page :string |
| :visits {:parameterized, Ch, :u64} |
| :visitors {:parameterized, Ch, :u64} |
| :active_visitors {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :exits {:parameterized, Ch, :u64} |
| :time_on_page {:parameterized, Ch, :u64} |
>]
"Plausible.Imported.SiteImport" [label= <| Plausible.Imported.SiteImport |
| site_imports |
| :id :id |
| :start_date :date |
| :end_date :date |
| :label :string |
| :source #Enum<[:csv, :google_analytics_4, :noop, :universal_analytics]> |
| :status #Enum<[:completed, :failed, :importing, :pending]> |
| :legacy :boolean |
| :site_id :id |
| :imported_by_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Imported.Source" [label= <| Plausible.Imported.Source |
| imported_sources |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :source :string |
| :referrer :string |
| :utm_source :string |
| :utm_medium :string |
| :utm_campaign :string |
| :utm_content :string |
| :utm_term :string |
| :visitors {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u32} |
>]
"Plausible.Imported.Visitor" [label= <| Plausible.Imported.Visitor |
| imported_visitors |
| :site_id {:parameterized, Ch, :u64} |
| :import_id {:parameterized, Ch, :u64} |
| :date :date |
| :visitors {:parameterized, Ch, :u64} |
| :pageviews {:parameterized, Ch, :u64} |
| :bounces {:parameterized, Ch, :u64} |
| :visits {:parameterized, Ch, :u64} |
| :visit_duration {:parameterized, Ch, :u64} |
>]
"Plausible.Ingestion.Counters.Record" [label= <| Plausible.Ingestion.Counters.Record |
| ingest_counters |
| :event_timebucket :utc_datetime |
| :site_id {:parameterized, Ch, {:nullable, :u64}} |
| :domain {:parameterized, Ch, {:low_cardinality, :string}} |
| :metric {:parameterized, Ch, {:low_cardinality, :string}} |
| :value {:parameterized, Ch, :u64} |
>]
"Plausible.Ingestion.Request" [label= <| Plausible.Ingestion.Request |
| :remote_ip :string |
| :user_agent :string |
| :event_name Plausible.Ecto.EventName |
| :uri :map |
| :hostname :string |
| :referrer :string |
| :domains {:array, :string} |
| :ip_classification :string |
| :hash_mode :integer |
| :pathname :string |
| :props :map |
| :revenue_source :map |
| :query_params :map |
| :timestamp :naive_datetime |
>]
"Plausible.Plugins.API.Token" [label= <| Plausible.Plugins.API.Token |
| plugins_api_tokens |
| :id :binary_id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
| :token_hash :binary |
| :description :string |
| :hint :string |
| :last_used_at :naive_datetime |
| :site_id :id |
>]
"Plausible.Shield.CountryRule" [label= <| Plausible.Shield.CountryRule |
| shield_rules_country |
| :id :binary_id |
| :site_id :id |
| :country_code :string |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.HostnameRule" [label= <| Plausible.Shield.HostnameRule |
| shield_rules_hostname |
| :id :binary_id |
| :site_id :id |
| :hostname :string |
| :hostname_pattern Plausible.Ecto.Types.CompiledRegex |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.IPRule" [label= <| Plausible.Shield.IPRule |
| shield_rules_ip |
| :id :binary_id |
| :site_id :id |
| :inet EctoNetwork.INET |
| :action #Enum<[:allow, :deny]> |
| :description :string |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Shield.PageRule" [label= <| Plausible.Shield.PageRule |
| shield_rules_page |
| :id :binary_id |
| :site_id :id |
| :page_path :string |
| :page_path_pattern Plausible.Ecto.Types.CompiledRegex |
| :action #Enum<[:allow, :deny]> |
| :added_by :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site" [label= <| Plausible.Site |
| sites |
| :id :id |
| :domain :string |
| :timezone :string |
| :public :boolean |
| :locked :boolean |
| :stats_start_date :date |
| :native_stats_start_at :naive_datetime |
| :allowed_event_props {:array, :string} |
| :conversions_enabled :boolean |
| :props_enabled :boolean |
| :funnels_enabled :boolean |
| :ingest_rate_limit_scale_seconds :integer |
| :ingest_rate_limit_threshold :integer |
| :domain_changed_from :string |
| :domain_changed_at :naive_datetime |
| :imported_data #Ecto.Embedded<[one: Plausible.Site.ImportedData]> |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.GoogleAuth" [label= <| Plausible.Site.GoogleAuth |
| google_auth |
| :id :id |
| :email :string |
| :property :string |
| :refresh_token :string |
| :access_token :string |
| :expires :naive_datetime |
| :user_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.ImportedData" [label= <| Plausible.Site.ImportedData |
| :id :binary_id |
| :start_date :date |
| :end_date :date |
| :source :string |
| :status :string |
>]
"Plausible.Site.Membership" [label= <| Plausible.Site.Membership |
| site_memberships |
| :id :id |
| :role #Enum<[:admin, :owner, :viewer]> |
| :site_id :id |
| :user_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.MonthlyReport" [label= <| Plausible.Site.MonthlyReport |
| monthly_reports |
| :id :id |
| :recipients {:array, :string} |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.SharedLink" [label= <| Plausible.Site.SharedLink |
| shared_links |
| :id :id |
| :site_id :id |
| :name :string |
| :slug :string |
| :password_hash :string |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.SpikeNotification" [label= <| Plausible.Site.SpikeNotification |
| spike_notifications |
| :id :id |
| :recipients {:array, :string} |
| :threshold :integer |
| :last_sent :naive_datetime |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.UserPreference" [label= <| Plausible.Site.UserPreference |
| site_user_preferences |
| :id :id |
| :pinned_at :naive_datetime |
| :user_id :id |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Site.WeeklyReport" [label= <| Plausible.Site.WeeklyReport |
| weekly_reports |
| :id :id |
| :recipients {:array, :string} |
| :site_id :id |
| :inserted_at :naive_datetime |
| :updated_at :naive_datetime |
>]
"Plausible.Funnel":"field@id":e -> "Plausible.Funnel.Step":"field@funnel_id":w
"Plausible.Goal":"field@id":e -> "Plausible.Funnel.Step":"field@goal_id":w
"Plausible.Site":"field@id":e -> "Plausible.Funnel":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Goal":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.GoogleAuth":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Auth.Invitation":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.MonthlyReport":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Plugins.API.Token":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.SharedLink":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.CountryRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.HostnameRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.IPRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Shield.PageRule":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Imported.SiteImport":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.Membership":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Site.UserPreference":"field@site_id":w
"Plausible.Site":"field@id":e -> "Plausible.Site.SpikeNotification":"field@site_id":w [dir=none]
"Plausible.Site":"field@id":e -> "Plausible.Site.WeeklyReport":"field@site_id":w [dir=none]
"Plausible.Site":"field@imported_data":e -> "Plausible.Site.ImportedData":"header@schema_module":w [dir=none]
"Plausible.Auth.User":"field@grace_period":e -> "Plausible.Auth.GracePeriod":"header@schema_module":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.ApiKey":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.EmailActivationCode":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Billing.EnterprisePlan":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.GoogleAuth":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.Invitation":"field@inviter_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Imported.SiteImport":"field@imported_by_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.Membership":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Site.UserPreference":"field@user_id":w
"Plausible.Auth.User":"field@id":e -> "Plausible.Billing.Subscription":"field@user_id":w [dir=none]
"Plausible.Auth.User":"field@id":e -> "Plausible.Auth.TOTP.RecoveryCode":"field@user_id":w
}
================================================
FILE: examples/dot/plausible-analytics/No-fields.dot
================================================
strict digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname="Roboto Mono"];
"Ecto.Migration.SchemaMigration" [label= <| Ecto.Migration.SchemaMigration |
| schema_migrations |
>]
"FunWithFlags.Store.Persistent.Ecto.Record" [label= <| FunWithFlags.Store.Persistent.Ecto.Record |
| fun_with_flags_toggles |
>]
"Oban.Job" [label= <>]
"Plausible.Auth.ApiKey" [label= <| Plausible.Auth.ApiKey |
| api_keys |
>]
"Plausible.Auth.EmailActivationCode" [label= <| Plausible.Auth.EmailActivationCode |
| email_activation_codes |
>]
"Plausible.Auth.GracePeriod" [label= <| Plausible.Auth.GracePeriod |
>]
"Plausible.Auth.Invitation" [label= <| Plausible.Auth.Invitation |
| invitations |
>]
"Plausible.Auth.TOTP.RecoveryCode" [label= <| Plausible.Auth.TOTP.RecoveryCode |
| totp_recovery_codes |
>]
"Plausible.Auth.User" [label= <| Plausible.Auth.User |
| users |
>]
"Plausible.Billing.EnterprisePlan" [label= <| Plausible.Billing.EnterprisePlan |
| enterprise_plans |
>]
"Plausible.Billing.Plan" [label= <>]
"Plausible.Billing.Subscription" [label= <| Plausible.Billing.Subscription |
| subscriptions |
>]
"Plausible.ClickhouseEventV2" [label= <| Plausible.ClickhouseEventV2 |
| events_v2 |
>]
"Plausible.ClickhouseSessionV2" [label= <| Plausible.ClickhouseSessionV2 |
| sessions_v2 |
>]
"Plausible.DataMigration.NumericIDs.DomainsLookup" [label= <| Plausible.DataMigration.NumericIDs.DomainsLookup |
| domains_lookup |
>]
"Plausible.Funnel" [label= <>]
"Plausible.Funnel.Step" [label= <| Plausible.Funnel.Step |
| funnel_steps |
>]
"Plausible.Goal" [label= <>]
"Plausible.Imported.Browser" [label= <| Plausible.Imported.Browser |
| imported_browsers |
>]
"Plausible.Imported.Device" [label= <| Plausible.Imported.Device |
| imported_devices |
>]
"Plausible.Imported.EntryPage" [label= <| Plausible.Imported.EntryPage |
| imported_entry_pages |
>]
"Plausible.Imported.ExitPage" [label= <| Plausible.Imported.ExitPage |
| imported_exit_pages |
>]
"Plausible.Imported.Location" [label= <| Plausible.Imported.Location |
| imported_locations |
>]
"Plausible.Imported.OperatingSystem" [label= <| Plausible.Imported.OperatingSystem |
| imported_operating_systems |
>]
"Plausible.Imported.Page" [label= <| Plausible.Imported.Page |
| imported_pages |
>]
"Plausible.Imported.SiteImport" [label= <| Plausible.Imported.SiteImport |
| site_imports |
>]
"Plausible.Imported.Source" [label= <| Plausible.Imported.Source |
| imported_sources |
>]
"Plausible.Imported.Visitor" [label= <| Plausible.Imported.Visitor |
| imported_visitors |
>]
"Plausible.Ingestion.Counters.Record" [label= <| Plausible.Ingestion.Counters.Record |
| ingest_counters |
>]
"Plausible.Ingestion.Request" [label= <| Plausible.Ingestion.Request |
>]
"Plausible.Plugins.API.Token" [label= <| Plausible.Plugins.API.Token |
| plugins_api_tokens |
>]
"Plausible.Shield.CountryRule" [label= <| Plausible.Shield.CountryRule |
| shield_rules_country |
>]
"Plausible.Shield.HostnameRule" [label= <| Plausible.Shield.HostnameRule |
| shield_rules_hostname |
>]
"Plausible.Shield.IPRule" [label= <| Plausible.Shield.IPRule |
| shield_rules_ip |
>]
"Plausible.Shield.PageRule" [label= <| Plausible.Shield.PageRule |
| shield_rules_page |
>]
"Plausible.Site" [label= <>]
"Plausible.Site.GoogleAuth" [label= <| Plausible.Site.GoogleAuth |
| google_auth |
>]
"Plausible.Site.ImportedData" [label= <| Plausible.Site.ImportedData |
>]
"Plausible.Site.Membership" [label= <| Plausible.Site.Membership |
| site_memberships |
>]
"Plausible.Site.MonthlyReport" [label= <| Plausible.Site.MonthlyReport |
| monthly_reports |
>]
"Plausible.Site.SharedLink" [label= <| Plausible.Site.SharedLink |
| shared_links |
>]
"Plausible.Site.SpikeNotification" [label= <| Plausible.Site.SpikeNotification |
| spike_notifications |
>]
"Plausible.Site.UserPreference" [label= <| Plausible.Site.UserPreference |
| site_user_preferences |
>]
"Plausible.Site.WeeklyReport" [label= <| Plausible.Site.WeeklyReport |
| weekly_reports |
>]
"Plausible.Funnel":e -> "Plausible.Funnel.Step":w
"Plausible.Goal":e -> "Plausible.Funnel.Step":w
"Plausible.Site":e -> "Plausible.Funnel":w
"Plausible.Site":e -> "Plausible.Goal":w
"Plausible.Site":e -> "Plausible.Site.GoogleAuth":w [dir=none]
"Plausible.Site":e -> "Plausible.Auth.Invitation":w
"Plausible.Site":e -> "Plausible.Site.MonthlyReport":w [dir=none]
"Plausible.Site":e -> "Plausible.Plugins.API.Token":w
"Plausible.Site":e -> "Plausible.Site.SharedLink":w
"Plausible.Site":e -> "Plausible.Shield.CountryRule":w
"Plausible.Site":e -> "Plausible.Shield.HostnameRule":w
"Plausible.Site":e -> "Plausible.Shield.IPRule":w
"Plausible.Site":e -> "Plausible.Shield.PageRule":w
"Plausible.Site":e -> "Plausible.Imported.SiteImport":w
"Plausible.Site":e -> "Plausible.Site.Membership":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.UserPreference":w
"Plausible.Site":e -> "Plausible.Site.SpikeNotification":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.WeeklyReport":w [dir=none]
"Plausible.Site":e -> "Plausible.Site.ImportedData":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.GracePeriod":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.ApiKey":w
"Plausible.Auth.User":e -> "Plausible.Auth.EmailActivationCode":w
"Plausible.Auth.User":e -> "Plausible.Billing.EnterprisePlan":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Site.GoogleAuth":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.Invitation":w
"Plausible.Auth.User":e -> "Plausible.Imported.SiteImport":w
"Plausible.Auth.User":e -> "Plausible.Site.Membership":w
"Plausible.Auth.User":e -> "Plausible.Site.UserPreference":w
"Plausible.Auth.User":e -> "Plausible.Billing.Subscription":w [dir=none]
"Plausible.Auth.User":e -> "Plausible.Auth.TOTP.RecoveryCode":w
}
================================================
FILE: examples/mermaid/changelog.com/Default.mmd
================================================
erDiagram
episodes {
integer id PK
varchar slug
varchar guid
varchar title
varchar subtitle
integer type
boolean featured
varchar highlight
varchar subhighlight
varchar summary
varchar notes
varchar doc_url
varchar socialize_url
boolean published
timestamp published_at
timestamp recorded_at
boolean recorded_live
varchar youtube_id
varchar cover
varchar audio_file
integer audio_bytes
integer audio_duration
jsonb audio_chapters
varchar plusplus_file
integer plusplus_bytes
integer plusplus_duration
jsonb plusplus_chapters
float download_count
float import_count
integer reach_count
varchar email_subject
varchar email_teaser
varchar email_content
integer email_sends
integer email_opens
array transcript
integer podcast_id
integer request_id
timestamp inserted_at
timestamp updated_at
}
episode_guests {
integer id PK
integer position
boolean thanks
varchar discount_code
integer episode_id
integer person_id
timestamp inserted_at
timestamp updated_at
}
episode_hosts {
integer id PK
integer position
integer person_id
integer episode_id
timestamp inserted_at
timestamp updated_at
}
episode_requests {
integer id PK
integer status
varchar hosts
varchar guests
varchar topics
varchar pitch
varchar pronunciation
varchar message
integer podcast_id
integer submitter_id
timestamp inserted_at
timestamp updated_at
}
episode_sponsors {
integer id PK
integer position
varchar title
varchar link_url
varchar description
float starts_at
float ends_at
integer episode_id
integer sponsor_id
timestamp inserted_at
timestamp updated_at
}
episode_stats {
integer id PK
date date
integer episode_bytes
integer total_bytes
float downloads
integer uniques
jsonb demographics
integer episode_id
integer podcast_id
timestamp inserted_at
timestamp updated_at
}
episode_topics {
integer id PK
integer position
integer topic_id
integer episode_id
timestamp inserted_at
timestamp updated_at
}
feeds {
integer id PK
varchar name
varchar slug
varchar description
varchar title_format
boolean plusplus
boolean autosub
timestamp starts_at
varchar cover
array podcast_ids
array person_ids
integer owner_id
timestamp inserted_at
timestamp updated_at
}
news_ads {
integer id PK
varchar url
varchar headline
varchar story
varchar image
boolean active
boolean newsletter
integer impression_count
integer click_count
integer sponsorship_id
timestamp inserted_at
timestamp updated_at
}
news_issues {
integer id PK
varchar slug
varchar note
varchar teaser
boolean published
timestamp published_at
timestamp inserted_at
timestamp updated_at
}
news_issue_ads {
integer id PK
integer position
boolean image
integer ad_id
integer issue_id
timestamp inserted_at
timestamp updated_at
}
news_issue_items {
integer id PK
integer position
boolean image
integer issue_id
integer item_id
timestamp inserted_at
timestamp updated_at
}
news_items {
integer id PK
integer status
integer type
varchar url
varchar headline
varchar story
varchar image
varchar object_id
boolean feed_only
boolean pinned
timestamp published_at
timestamp refreshed_at
integer impression_count
integer click_count
varchar message
integer author_id
integer logger_id
integer submitter_id
integer source_id
timestamp inserted_at
timestamp updated_at
}
news_item_comments {
integer id PK
varchar content
boolean approved
timestamp edited_at
timestamp deleted_at
integer item_id
integer author_id
integer parent_id
timestamp inserted_at
timestamp updated_at
}
news_item_topics {
integer id PK
integer position
integer item_id
integer topic_id
timestamp inserted_at
timestamp updated_at
}
news_queue {
integer id PK
float position
integer item_id
}
news_sources {
integer id PK
varchar name
varchar slug
varchar website
varchar twitter_handle
varchar description
varchar feed
varchar regex
boolean publication
varchar icon
timestamp inserted_at
timestamp updated_at
}
news_sponsorships {
integer id PK
varchar name
array weeks
integer impression_count
integer click_count
integer sponsor_id
timestamp inserted_at
timestamp updated_at
}
people {
integer id PK
varchar name
varchar email
varchar handle
varchar github_handle
varchar linkedin_handle
varchar mastodon_handle
varchar twitter_handle
varchar slack_id
varchar website
varchar bio
varchar location
varchar auth_token
timestamp auth_token_expires_at
timestamp joined_at
timestamp signed_in_at
boolean approved
varchar avatar
boolean admin
boolean host
boolean editor
boolean public_profile
jsonb settings
timestamp inserted_at
timestamp updated_at
}
podcasts {
integer id PK
varchar name
varchar slug
integer status
varchar welcome
varchar description
varchar extended_description
varchar vanity_domain
varchar keywords
varchar mastodon_handle
varchar twitter_handle
varchar apple_url
varchar spotify_url
varchar riverside_url
varchar chartable_id
varchar schedule_note
float download_count
integer reach_count
boolean recorded_live
boolean partner
integer position
jsonb subscribers
varchar cover
timestamp inserted_at
timestamp updated_at
}
podcast_hosts {
integer id PK
integer position
boolean retired
integer person_id
integer podcast_id
timestamp inserted_at
timestamp updated_at
}
podcast_topics {
integer id PK
integer position
integer podcast_id
integer topic_id
timestamp inserted_at
timestamp updated_at
}
posts {
integer id PK
varchar title
varchar subtitle
varchar slug
varchar guid
varchar canonical_url
varchar image
varchar tldr
varchar body
boolean published
timestamp published_at
integer author_id
integer editor_id
timestamp inserted_at
timestamp updated_at
}
post_topics {
integer id PK
integer position
integer topic_id
integer post_id
timestamp inserted_at
timestamp updated_at
}
sponsors {
integer id PK
varchar name
varchar description
varchar website
varchar github_handle
varchar twitter_handle
varchar avatar
varchar color_logo
varchar dark_logo
varchar light_logo
timestamp inserted_at
timestamp updated_at
}
sponsor_reps {
integer id PK
integer sponsor_id
integer person_id
timestamp inserted_at
timestamp updated_at
}
subscriptions {
integer id PK
timestamp unsubscribed_at
varchar context
integer episode_id
integer item_id
integer person_id
integer podcast_id
timestamp inserted_at
timestamp updated_at
}
topics {
integer id PK
varchar name
varchar slug
varchar description
varchar website
varchar twitter_handle
varchar icon
timestamp inserted_at
timestamp updated_at
}
schema_migrations {
integer version PK
timestamp inserted_at
}
oban_jobs {
integer id PK
varchar state
varchar queue
varchar worker
jsonb args
jsonb meta
array tags
array errors
integer attempt
array attempted_by
integer max_attempts
integer priority
timestamp attempted_at
timestamp cancelled_at
timestamp completed_at
timestamp discarded_at
timestamp inserted_at
timestamp scheduled_at
}
episode_requests ||--o| episodes : ""
episodes ||--|{ episode_guests : ""
episodes ||--|{ episode_hosts : ""
episodes ||--|{ episode_sponsors : ""
episodes ||--|{ episode_stats : ""
episodes ||--|{ episode_topics : ""
episodes ||--|{ subscriptions : ""
news_ads ||--|{ news_issue_ads : ""
news_issues ||--|{ news_issue_ads : ""
news_issues ||--|{ news_issue_items : ""
news_item_comments ||--|{ news_item_comments : ""
news_items ||--|{ news_issue_items : ""
news_items ||--|{ news_item_comments : ""
news_items ||--|{ news_item_topics : ""
news_items ||--o| news_queue : ""
news_items ||--|{ subscriptions : ""
news_sources ||--|{ news_items : ""
news_sponsorships ||--|{ news_ads : ""
people ||--|{ episode_guests : ""
people ||--|{ episode_hosts : ""
people ||--|{ episode_requests : ""
people ||--|{ feeds : ""
people ||--|{ news_item_comments : ""
people ||--|{ news_items : ""
people ||--|{ news_items : ""
people ||--|{ news_items : ""
people ||--|{ podcast_hosts : ""
people ||--|{ posts : ""
people ||--|{ posts : ""
people ||--|{ sponsor_reps : ""
people ||--|{ subscriptions : ""
podcasts ||--|{ episode_requests : ""
podcasts ||--|{ episode_stats : ""
podcasts ||--|{ episodes : ""
podcasts ||--|{ podcast_hosts : ""
podcasts ||--|{ podcast_topics : ""
podcasts ||--|{ subscriptions : ""
posts ||--|{ post_topics : ""
sponsors ||--|{ episode_sponsors : ""
sponsors ||--|{ news_sponsorships : ""
sponsors ||--|{ sponsor_reps : ""
topics ||--|{ episode_topics : ""
topics ||--|{ news_item_topics : ""
topics ||--|{ podcast_topics : ""
topics ||--|{ post_topics : ""
================================================
FILE: examples/mermaid/changelog.com/No-fields.mmd
================================================
erDiagram
episodes
episode_guests
episode_hosts
episode_requests
episode_sponsors
episode_stats
episode_topics
feeds
news_ads
news_issues
news_issue_ads
news_issue_items
news_items
news_item_comments
news_item_topics
news_queue
news_sources
news_sponsorships
people
podcasts
podcast_hosts
podcast_topics
posts
post_topics
sponsors
sponsor_reps
subscriptions
topics
schema_migrations
oban_jobs
episode_requests ||--o| episodes : ""
episodes ||--|{ episode_guests : ""
episodes ||--|{ episode_hosts : ""
episodes ||--|{ episode_sponsors : ""
episodes ||--|{ episode_stats : ""
episodes ||--|{ episode_topics : ""
episodes ||--|{ subscriptions : ""
news_ads ||--|{ news_issue_ads : ""
news_issues ||--|{ news_issue_ads : ""
news_issues ||--|{ news_issue_items : ""
news_item_comments ||--|{ news_item_comments : ""
news_items ||--|{ news_issue_items : ""
news_items ||--|{ news_item_comments : ""
news_items ||--|{ news_item_topics : ""
news_items ||--o| news_queue : ""
news_items ||--|{ subscriptions : ""
news_sources ||--|{ news_items : ""
news_sponsorships ||--|{ news_ads : ""
people ||--|{ episode_guests : ""
people ||--|{ episode_hosts : ""
people ||--|{ episode_requests : ""
people ||--|{ feeds : ""
people ||--|{ news_item_comments : ""
people ||--|{ news_items : ""
people ||--|{ news_items : ""
people ||--|{ news_items : ""
people ||--|{ podcast_hosts : ""
people ||--|{ posts : ""
people ||--|{ posts : ""
people ||--|{ sponsor_reps : ""
people ||--|{ subscriptions : ""
podcasts ||--|{ episode_requests : ""
podcasts ||--|{ episode_stats : ""
podcasts ||--|{ episodes : ""
podcasts ||--|{ podcast_hosts : ""
podcasts ||--|{ podcast_topics : ""
podcasts ||--|{ subscriptions : ""
posts ||--|{ post_topics : ""
sponsors ||--|{ episode_sponsors : ""
sponsors ||--|{ news_sponsorships : ""
sponsors ||--|{ sponsor_reps : ""
topics ||--|{ episode_topics : ""
topics ||--|{ news_item_topics : ""
topics ||--|{ podcast_topics : ""
topics ||--|{ post_topics : ""
================================================
FILE: examples/mermaid/hexpm/Default.mmd
================================================
erDiagram
schema_migrations {
integer version PK
timestamp inserted_at
}
audit_logs {
integer id PK
varchar user_agent
varchar remote_ip
varchar action
jsonb params
integer user_id
integer organization_id
integer key_id
timestamp inserted_at
}
emails {
integer id PK
varchar email
boolean verified
boolean primary
boolean public
boolean gravatar
varchar verification_key
timestamp verification_expiry
integer user_id
timestamp inserted_at
timestamp updated_at
}
keys {
integer id PK
varchar name
varchar secret_first
varchar secret_second
boolean public
timestamp revoke_at
timestamp inserted_at
timestamp updated_at
jsonb last_use
integer user_id
integer organization_id
jsonb permissions
}
organizations {
integer id PK
varchar name
boolean billing_active
boolean billing_override
timestamp trial_end
timestamp inserted_at
timestamp updated_at
}
organization_users {
integer id PK
varchar role
integer organization_id
integer user_id
timestamp inserted_at
timestamp updated_at
}
password_resets {
integer id PK
varchar key
varchar primary_email
integer user_id
timestamp inserted_at
}
sessions {
integer id PK
bytea token
jsonb data
timestamp inserted_at
timestamp updated_at
}
users {
integer id PK
varchar username
varchar full_name
varchar password
boolean service
timestamp deactivated_at
varchar role
timestamp inserted_at
timestamp updated_at
jsonb handles
jsonb tfa
integer organization_id
}
blocked_addresses {
integer id PK
varchar ip
varchar comment
}
downloads {
integer id PK
integer package_id
integer release_id
integer downloads
date day
}
installs {
integer id PK
varchar hex
array elixirs
}
packages {
integer id PK
varchar name
timestamp docs_updated_at
timestamp inserted_at
timestamp updated_at
integer repository_id
jsonb meta
}
package_dependants {
integer id PK
integer package_id
varchar name
varchar repo
}
package_downloads {
integer package_id
varchar view
integer downloads
}
package_owners {
integer id PK
varchar level
integer package_id
integer user_id
timestamp inserted_at
timestamp updated_at
}
package_reports {
integer id PK
varchar state
varchar description
integer author_id
integer package_id
timestamp inserted_at
timestamp updated_at
}
package_report_comments {
integer id PK
varchar text
timestamp inserted_at
timestamp updated_at
integer package_report_id
integer author_id
}
package_report_releases {
integer id PK
integer release_id
integer package_report_id
timestamp inserted_at
timestamp updated_at
}
releases {
integer id PK
varchar version
bytea inner_checksum
bytea outer_checksum
boolean has_docs
timestamp inserted_at
timestamp updated_at
integer package_id
integer publisher_id
jsonb meta
jsonb retirement
}
release_downloads {
integer package_id
integer release_id
integer downloads
}
repositories {
integer id PK
varchar name
timestamp inserted_at
timestamp updated_at
integer organization_id
}
requirements {
integer id PK
varchar app
varchar requirement
boolean optional
integer release_id
integer dependency_id
}
short_urls {
integer id PK
varchar url
varchar short_code
timestamp inserted_at
}
keys ||--|{ audit_logs : ""
organizations ||--|{ audit_logs : ""
organizations ||--|{ keys : ""
organizations ||--|{ organization_users : ""
organizations ||--o| repositories : ""
organizations ||--o| users : ""
package_reports ||--|{ package_report_comments : ""
package_reports ||--|{ package_report_releases : ""
packages ||--|{ downloads : ""
packages ||--|{ package_dependants : ""
packages ||--|{ package_downloads : ""
packages ||--|{ package_owners : ""
packages ||--|{ package_reports : ""
packages ||--|{ release_downloads : ""
packages ||--|{ releases : ""
packages ||--|{ requirements : ""
releases ||--|{ downloads : ""
releases ||--|{ package_report_releases : ""
releases ||--o| release_downloads : ""
releases ||--|{ requirements : ""
repositories ||--|{ packages : ""
users ||--|{ audit_logs : ""
users ||--|{ emails : ""
users ||--|{ keys : ""
users ||--|{ organization_users : ""
users ||--|{ package_owners : ""
users ||--|{ package_report_comments : ""
users ||--|{ package_reports : ""
users ||--|{ password_resets : ""
users ||--|{ releases : ""
================================================
FILE: examples/mermaid/hexpm/No-fields.mmd
================================================
erDiagram
schema_migrations
audit_logs
emails
keys
organizations
organization_users
password_resets
sessions
users
blocked_addresses
downloads
installs
packages
package_dependants
package_downloads
package_owners
package_reports
package_report_comments
package_report_releases
releases
release_downloads
repositories
requirements
short_urls
keys ||--|{ audit_logs : ""
organizations ||--|{ audit_logs : ""
organizations ||--|{ keys : ""
organizations ||--|{ organization_users : ""
organizations ||--o| repositories : ""
organizations ||--o| users : ""
package_reports ||--|{ package_report_comments : ""
package_reports ||--|{ package_report_releases : ""
packages ||--|{ downloads : ""
packages ||--|{ package_dependants : ""
packages ||--|{ package_downloads : ""
packages ||--|{ package_owners : ""
packages ||--|{ package_reports : ""
packages ||--|{ release_downloads : ""
packages ||--|{ releases : ""
packages ||--|{ requirements : ""
releases ||--|{ downloads : ""
releases ||--|{ package_report_releases : ""
releases ||--o| release_downloads : ""
releases ||--|{ requirements : ""
repositories ||--|{ packages : ""
users ||--|{ audit_logs : ""
users ||--|{ emails : ""
users ||--|{ keys : ""
users ||--|{ organization_users : ""
users ||--|{ package_owners : ""
users ||--|{ package_report_comments : ""
users ||--|{ package_reports : ""
users ||--|{ password_resets : ""
users ||--|{ releases : ""
================================================
FILE: examples/mermaid/plausible-analytics/Default.mmd
================================================
erDiagram
schema_migrations {
integer version PK
timestamp inserted_at
}
fun_with_flags_toggles {
integer id PK
varchar flag_name
varchar gate_type
varchar target
boolean enabled
}
oban_jobs {
integer id PK
varchar state
varchar queue
varchar worker
jsonb args
jsonb meta
array tags
array errors
integer attempt
array attempted_by
integer max_attempts
integer priority
timestamp attempted_at
timestamp cancelled_at
timestamp completed_at
timestamp discarded_at
timestamp inserted_at
timestamp scheduled_at
}
api_keys {
integer id PK
varchar name
array scopes
integer hourly_request_limit
varchar key_hash
varchar key_prefix
integer user_id
timestamp inserted_at
timestamp updated_at
}
email_activation_codes {
integer id PK
varchar code
timestamp issued_at
integer user_id
}
invitations {
integer id PK
varchar invitation_id
varchar email
varchar role
integer inviter_id
integer site_id
timestamp inserted_at
timestamp updated_at
}
totp_recovery_codes {
integer id PK
varchar code_digest
integer user_id
timestamp inserted_at
}
users {
integer id PK
varchar email
varchar password_hash
varchar name
timestamp last_seen
date trial_expiry_date
varchar theme
boolean email_verified
varchar previous_email
date accept_traffic_until
boolean allow_next_upgrade_override
boolean totp_enabled
bytea totp_secret
varchar totp_token
timestamp totp_last_used_at
jsonb grace_period
timestamp inserted_at
timestamp updated_at
}
enterprise_plans {
integer id PK
varchar paddle_plan_id
varchar billing_interval
integer monthly_pageview_limit
integer site_limit
integer team_member_limit
array features
integer hourly_api_request_limit
integer user_id
timestamp inserted_at
timestamp updated_at
}
subscriptions {
integer id PK
varchar paddle_subscription_id
varchar paddle_plan_id
varchar update_url
varchar cancel_url
varchar status
varchar next_bill_amount
date next_bill_date
date last_bill_date
varchar currency_code
integer user_id
timestamp inserted_at
timestamp updated_at
}
events_v2 {
unknown name
unknown site_id
varchar hostname
varchar pathname
unknown user_id
unknown session_id
timestamp timestamp
unknown revenue_source_amount
unknown revenue_source_currency
unknown revenue_reporting_amount
unknown revenue_reporting_currency
varchar referrer
varchar referrer_source
varchar utm_medium
varchar utm_source
varchar utm_campaign
varchar utm_content
varchar utm_term
unknown country_code
unknown subdivision1_code
unknown subdivision2_code
unknown city_geoname_id
unknown screen_size
unknown operating_system
unknown operating_system_version
unknown browser
unknown browser_version
}
sessions_v2 {
varchar hostname
unknown site_id
unknown user_id
unknown session_id
timestamp start
unknown duration
unknown is_bounce
varchar entry_page
varchar exit_page
varchar exit_page_hostname
unknown pageviews
unknown events
unknown sign
varchar utm_medium
varchar utm_source
varchar utm_campaign
varchar utm_content
varchar utm_term
varchar referrer
varchar referrer_source
unknown country_code
unknown subdivision1_code
unknown subdivision2_code
unknown city_geoname_id
unknown screen_size
unknown operating_system
unknown operating_system_version
unknown browser
unknown browser_version
timestamp timestamp
varchar transferred_from
}
domains_lookup {
unknown site_id
varchar domain
}
funnels {
integer id PK
varchar name
integer site_id
timestamp inserted_at
timestamp updated_at
}
funnel_steps {
integer id PK
integer step_order
integer funnel_id
integer goal_id
timestamp inserted_at
timestamp updated_at
}
goals {
integer id PK
varchar event_name
varchar page_path
varchar currency
integer site_id
timestamp inserted_at
timestamp updated_at
}
imported_browsers {
unknown site_id
unknown import_id
date date
varchar browser
varchar browser_version
unknown visitors
unknown visits
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_devices {
unknown site_id
unknown import_id
date date
varchar device
unknown visitors
unknown visits
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_entry_pages {
unknown site_id
unknown import_id
date date
varchar entry_page
unknown visitors
unknown entrances
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_exit_pages {
unknown site_id
unknown import_id
date date
varchar exit_page
unknown exits
unknown visitors
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_locations {
unknown site_id
unknown import_id
date date
varchar country
varchar region
unknown city
unknown visitors
unknown visits
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_operating_systems {
unknown site_id
unknown import_id
date date
varchar operating_system
varchar operating_system_version
unknown visitors
unknown visits
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_pages {
unknown site_id
unknown import_id
date date
varchar hostname
varchar page
unknown visits
unknown visitors
unknown active_visitors
unknown pageviews
unknown exits
unknown time_on_page
}
site_imports {
integer id PK
date start_date
date end_date
varchar label
varchar source
varchar status
boolean legacy
integer site_id
integer imported_by_id
timestamp inserted_at
timestamp updated_at
}
imported_sources {
unknown site_id
unknown import_id
date date
varchar source
varchar referrer
varchar utm_source
varchar utm_medium
varchar utm_campaign
varchar utm_content
varchar utm_term
unknown visitors
unknown visits
unknown visit_duration
unknown pageviews
unknown bounces
}
imported_visitors {
unknown site_id
unknown import_id
date date
unknown visitors
unknown pageviews
unknown bounces
unknown visits
unknown visit_duration
}
ingest_counters {
timestamp event_timebucket
unknown site_id
unknown domain
unknown metric
unknown value
}
plugins_api_tokens {
uuid id PK
timestamp inserted_at
timestamp updated_at
bytea token_hash
varchar description
varchar hint
timestamp last_used_at
integer site_id
}
shield_rules_country {
uuid id PK
integer site_id
varchar country_code
varchar action
varchar added_by
timestamp inserted_at
timestamp updated_at
}
shield_rules_hostname {
uuid id PK
integer site_id
varchar hostname
varchar hostname_pattern
varchar action
varchar added_by
timestamp inserted_at
timestamp updated_at
}
shield_rules_ip {
uuid id PK
integer site_id
inet inet
varchar action
varchar description
varchar added_by
timestamp inserted_at
timestamp updated_at
}
shield_rules_page {
uuid id PK
integer site_id
varchar page_path
varchar page_path_pattern
varchar action
varchar added_by
timestamp inserted_at
timestamp updated_at
}
sites {
integer id PK
varchar domain
varchar timezone
boolean public
boolean locked
date stats_start_date
timestamp native_stats_start_at
array allowed_event_props
boolean conversions_enabled
boolean props_enabled
boolean funnels_enabled
integer ingest_rate_limit_scale_seconds
integer ingest_rate_limit_threshold
varchar domain_changed_from
timestamp domain_changed_at
jsonb imported_data
timestamp inserted_at
timestamp updated_at
}
google_auth {
integer id PK
varchar email
varchar property
varchar refresh_token
varchar access_token
timestamp expires
integer user_id
integer site_id
timestamp inserted_at
timestamp updated_at
}
site_memberships {
integer id PK
varchar role
integer site_id
integer user_id
timestamp inserted_at
timestamp updated_at
}
monthly_reports {
integer id PK
array recipients
integer site_id
timestamp inserted_at
timestamp updated_at
}
shared_links {
integer id PK
integer site_id
varchar name
varchar slug
varchar password_hash
timestamp inserted_at
timestamp updated_at
}
spike_notifications {
integer id PK
array recipients
integer threshold
timestamp last_sent
integer site_id
timestamp inserted_at
timestamp updated_at
}
site_user_preferences {
integer id PK
timestamp pinned_at
integer user_id
integer site_id
timestamp inserted_at
timestamp updated_at
}
weekly_reports {
integer id PK
array recipients
integer site_id
timestamp inserted_at
timestamp updated_at
}
funnels ||--|{ funnel_steps : ""
goals ||--|{ funnel_steps : ""
sites ||--|{ funnels : ""
sites ||--|{ goals : ""
sites ||--o| google_auth : ""
sites ||--|{ invitations : ""
sites ||--o| monthly_reports : ""
sites ||--|{ plugins_api_tokens : ""
sites ||--|{ shared_links : ""
sites ||--|{ shield_rules_country : ""
sites ||--|{ shield_rules_hostname : ""
sites ||--|{ shield_rules_ip : ""
sites ||--|{ shield_rules_page : ""
sites ||--|{ site_imports : ""
sites ||--o| site_memberships : ""
sites ||--|{ site_user_preferences : ""
sites ||--o| spike_notifications : ""
sites ||--o| weekly_reports : ""
users ||--|{ api_keys : ""
users ||--|{ email_activation_codes : ""
users ||--o| enterprise_plans : ""
users ||--o| google_auth : ""
users ||--|{ invitations : ""
users ||--|{ site_imports : ""
users ||--|{ site_memberships : ""
users ||--|{ site_user_preferences : ""
users ||--o| subscriptions : ""
users ||--|{ totp_recovery_codes : ""
================================================
FILE: examples/mermaid/plausible-analytics/No-fields.mmd
================================================
erDiagram
schema_migrations
fun_with_flags_toggles
oban_jobs
api_keys
email_activation_codes
invitations
totp_recovery_codes
users
enterprise_plans
subscriptions
events_v2
sessions_v2
domains_lookup
funnels
funnel_steps
goals
imported_browsers
imported_devices
imported_entry_pages
imported_exit_pages
imported_locations
imported_operating_systems
imported_pages
site_imports
imported_sources
imported_visitors
ingest_counters
plugins_api_tokens
shield_rules_country
shield_rules_hostname
shield_rules_ip
shield_rules_page
sites
google_auth
site_memberships
monthly_reports
shared_links
spike_notifications
site_user_preferences
weekly_reports
funnels ||--|{ funnel_steps : ""
goals ||--|{ funnel_steps : ""
sites ||--|{ funnels : ""
sites ||--|{ goals : ""
sites ||--o| google_auth : ""
sites ||--|{ invitations : ""
sites ||--o| monthly_reports : ""
sites ||--|{ plugins_api_tokens : ""
sites ||--|{ shared_links : ""
sites ||--|{ shield_rules_country : ""
sites ||--|{ shield_rules_hostname : ""
sites ||--|{ shield_rules_ip : ""
sites ||--|{ shield_rules_page : ""
sites ||--|{ site_imports : ""
sites ||--o| site_memberships : ""
sites ||--|{ site_user_preferences : ""
sites ||--o| spike_notifications : ""
sites ||--o| weekly_reports : ""
users ||--|{ api_keys : ""
users ||--|{ email_activation_codes : ""
users ||--o| enterprise_plans : ""
users ||--o| google_auth : ""
users ||--|{ invitations : ""
users ||--|{ site_imports : ""
users ||--|{ site_memberships : ""
users ||--|{ site_user_preferences : ""
users ||--o| subscriptions : ""
users ||--|{ totp_recovery_codes : ""
================================================
FILE: examples/plantuml/changelog.com/Clusters.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace EPISODE #b4eeb4 {
entity Changelog.Episode {
id : id
--
slug : string
guid : string
title : string
subtitle : string
type : integer
featured : boolean
highlight : string
subhighlight : string
summary : string
notes : string
doc_url : string
socialize_url : string
published : boolean
published_at : utc_datetime
recorded_at : utc_datetime
recorded_live : boolean
youtube_id : string
cover : string
audio_file : string
audio_bytes : integer
audio_duration : integer
audio_chapters : map
plusplus_file : string
plusplus_bytes : integer
plusplus_duration : integer
plusplus_chapters : map
download_count : float
import_count : float
reach_count : integer
email_subject : string
email_teaser : string
email_content : string
email_sends : integer
email_opens : integer
transcript : array
podcast_id : id
request_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeChapter {
id : binary_id
--
title : string
starts_at : float
ends_at : float
link_url : string
image_url : string
}
entity Changelog.EpisodeGuest {
id : id
--
position : integer
thanks : boolean
discount_code : string
episode_id : id
person_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeHost {
id : id
--
position : integer
person_id : id
episode_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeRequest {
id : id
--
status : integer
hosts : string
guests : string
topics : string
pitch : string
pronunciation : string
message : string
podcast_id : id
submitter_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeSponsor {
id : id
--
position : integer
title : string
link_url : string
description : string
starts_at : float
ends_at : float
episode_id : id
sponsor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeStat {
id : id
--
date : date
episode_bytes : integer
total_bytes : integer
downloads : float
uniques : integer
demographics : map
episode_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeTopic {
id : id
--
position : integer
topic_id : id
episode_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace NEWS #eee5de {
entity Changelog.NewsAd {
id : id
--
url : string
headline : string
story : string
image : string
active : boolean
newsletter : boolean
impression_count : integer
click_count : integer
sponsorship_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssue {
id : id
--
slug : string
note : string
teaser : string
published : boolean
published_at : utc_datetime
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssueAd {
id : id
--
position : integer
image : boolean
ad_id : id
issue_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssueItem {
id : id
--
position : integer
image : boolean
issue_id : id
item_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItem {
id : id
--
status : integer
type : integer
url : string
headline : string
story : string
image : string
object_id : string
feed_only : boolean
pinned : boolean
published_at : utc_datetime
refreshed_at : utc_datetime
impression_count : integer
click_count : integer
message : string
author_id : id
logger_id : id
submitter_id : id
source_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItemComment {
id : id
--
content : string
approved : boolean
edited_at : utc_datetime
deleted_at : utc_datetime
item_id : id
author_id : id
parent_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItemTopic {
id : id
--
position : integer
item_id : id
topic_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsQueue {
id : id
--
position : float
item_id : id
}
entity Changelog.NewsSource {
id : id
--
name : string
slug : string
website : string
twitter_handle : string
description : string
feed : string
regex : string
publication : boolean
icon : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsSponsorship {
id : id
--
name : string
weeks : array
impression_count : integer
click_count : integer
sponsor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace PERSON #f0ffff {
entity Changelog.Person {
id : id
--
name : string
email : string
handle : string
github_handle : string
linkedin_handle : string
mastodon_handle : string
twitter_handle : string
slack_id : string
website : string
bio : string
location : string
auth_token : string
auth_token_expires_at : utc_datetime
joined_at : utc_datetime
signed_in_at : utc_datetime
approved : boolean
avatar : string
admin : boolean
host : boolean
editor : boolean
public_profile : boolean
settings : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Person.Settings {
subscribe_to_contributed_news : boolean
subscribe_to_participated_episodes : boolean
email_on_authored_news : boolean
email_on_submitted_news : boolean
email_on_comment_replies : boolean
email_on_comment_mentions : boolean
}
}
namespace PODCAST #ffefd5 {
entity Changelog.Podcast {
id : id
--
name : string
slug : string
status : integer
welcome : string
description : string
extended_description : string
vanity_domain : string
keywords : string
mastodon_handle : string
twitter_handle : string
apple_url : string
spotify_url : string
riverside_url : string
chartable_id : string
schedule_note : string
download_count : float
reach_count : integer
recorded_live : boolean
partner : boolean
position : integer
subscribers : map
cover : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PodcastHost {
id : id
--
position : integer
retired : boolean
person_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PodcastTopic {
id : id
--
position : integer
podcast_id : id
topic_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace POST #eee5de {
entity Changelog.Post {
id : id
--
title : string
subtitle : string
slug : string
guid : string
canonical_url : string
image : string
tldr : string
body : string
published : boolean
published_at : utc_datetime
author_id : id
editor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PostTopic {
id : id
--
position : integer
topic_id : id
post_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace SPONSOR #fffafa {
entity Changelog.Sponsor {
id : id
--
name : string
description : string
website : string
github_handle : string
twitter_handle : string
avatar : string
color_logo : string
dark_logo : string
light_logo : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.SponsorRep {
id : id
--
sponsor_id : id
person_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
entity Changelog.Feed {
id : id
--
name : string
slug : string
description : string
title_format : string
plusplus : boolean
autosub : boolean
starts_at : utc_datetime
cover : string
podcast_ids : array
person_ids : array
owner_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Subscription {
id : id
--
unsubscribed_at : utc_datetime
context : string
episode_id : id
item_id : id
person_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Topic {
id : id
--
name : string
slug : string
description : string
website : string
twitter_handle : string
icon : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
entity Oban.Job {
id : id
--
state : string
queue : string
worker : string
args : map
meta : map
tags : array
errors : array
attempt : integer
attempted_by : array
max_attempts : integer
priority : integer
attempted_at : utc_datetime_usec
cancelled_at : utc_datetime_usec
completed_at : utc_datetime_usec
discarded_at : utc_datetime_usec
inserted_at : utc_datetime_usec
scheduled_at : utc_datetime_usec
}
Changelog.EpisodeRequest ||--o| Changelog.Episode
Changelog.Episode ||--|{ Changelog.EpisodeChapter
Changelog.Episode ||--|{ Changelog.EpisodeGuest
Changelog.Episode ||--|{ Changelog.EpisodeHost
Changelog.Episode ||--|{ Changelog.EpisodeSponsor
Changelog.Episode ||--|{ Changelog.EpisodeStat
Changelog.Episode ||--|{ Changelog.EpisodeTopic
Changelog.Episode ||--|{ Changelog.Subscription
Changelog.NewsAd ||--|{ Changelog.NewsIssueAd
Changelog.NewsIssue ||--|{ Changelog.NewsIssueAd
Changelog.NewsIssue ||--|{ Changelog.NewsIssueItem
Changelog.NewsItemComment ||--|{ Changelog.NewsItemComment
Changelog.NewsItem ||--|{ Changelog.NewsIssueItem
Changelog.NewsItem ||--|{ Changelog.NewsItemComment
Changelog.NewsItem ||--|{ Changelog.NewsItemTopic
Changelog.NewsItem ||--o| Changelog.NewsQueue
Changelog.NewsItem ||--|{ Changelog.Subscription
Changelog.NewsSource ||--|{ Changelog.NewsItem
Changelog.NewsSponsorship ||--|{ Changelog.NewsAd
Changelog.Person ||--|{ Changelog.EpisodeGuest
Changelog.Person ||--|{ Changelog.EpisodeHost
Changelog.Person ||--|{ Changelog.EpisodeRequest
Changelog.Person ||--|{ Changelog.Feed
Changelog.Person ||--|{ Changelog.NewsItemComment
Changelog.Person ||--|{ Changelog.NewsItem
Changelog.Person ||--|{ Changelog.PodcastHost
Changelog.Person ||--|{ Changelog.Post
Changelog.Person ||--|{ Changelog.SponsorRep
Changelog.Person ||--|{ Changelog.Subscription
Changelog.Person ||--o| Changelog.Person.Settings
Changelog.Podcast ||--|{ Changelog.EpisodeRequest
Changelog.Podcast ||--|{ Changelog.EpisodeStat
Changelog.Podcast ||--|{ Changelog.Episode
Changelog.Podcast ||--|{ Changelog.PodcastHost
Changelog.Podcast ||--|{ Changelog.PodcastTopic
Changelog.Podcast ||--|{ Changelog.Subscription
Changelog.Post ||--|{ Changelog.PostTopic
Changelog.Sponsor ||--|{ Changelog.EpisodeSponsor
Changelog.Sponsor ||--|{ Changelog.NewsSponsorship
Changelog.Sponsor ||--|{ Changelog.SponsorRep
Changelog.Topic ||--|{ Changelog.EpisodeTopic
Changelog.Topic ||--|{ Changelog.NewsItemTopic
Changelog.Topic ||--|{ Changelog.PodcastTopic
Changelog.Topic ||--|{ Changelog.PostTopic
@enduml
================================================
FILE: examples/plantuml/changelog.com/Default.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
entity Changelog.Episode {
id : id
--
slug : string
guid : string
title : string
subtitle : string
type : integer
featured : boolean
highlight : string
subhighlight : string
summary : string
notes : string
doc_url : string
socialize_url : string
published : boolean
published_at : utc_datetime
recorded_at : utc_datetime
recorded_live : boolean
youtube_id : string
cover : string
audio_file : string
audio_bytes : integer
audio_duration : integer
audio_chapters : map
plusplus_file : string
plusplus_bytes : integer
plusplus_duration : integer
plusplus_chapters : map
download_count : float
import_count : float
reach_count : integer
email_subject : string
email_teaser : string
email_content : string
email_sends : integer
email_opens : integer
transcript : array
podcast_id : id
request_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeChapter {
id : binary_id
--
title : string
starts_at : float
ends_at : float
link_url : string
image_url : string
}
entity Changelog.EpisodeGuest {
id : id
--
position : integer
thanks : boolean
discount_code : string
episode_id : id
person_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeHost {
id : id
--
position : integer
person_id : id
episode_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeRequest {
id : id
--
status : integer
hosts : string
guests : string
topics : string
pitch : string
pronunciation : string
message : string
podcast_id : id
submitter_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeSponsor {
id : id
--
position : integer
title : string
link_url : string
description : string
starts_at : float
ends_at : float
episode_id : id
sponsor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeStat {
id : id
--
date : date
episode_bytes : integer
total_bytes : integer
downloads : float
uniques : integer
demographics : map
episode_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.EpisodeTopic {
id : id
--
position : integer
topic_id : id
episode_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Feed {
id : id
--
name : string
slug : string
description : string
title_format : string
plusplus : boolean
autosub : boolean
starts_at : utc_datetime
cover : string
podcast_ids : array
person_ids : array
owner_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsAd {
id : id
--
url : string
headline : string
story : string
image : string
active : boolean
newsletter : boolean
impression_count : integer
click_count : integer
sponsorship_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssue {
id : id
--
slug : string
note : string
teaser : string
published : boolean
published_at : utc_datetime
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssueAd {
id : id
--
position : integer
image : boolean
ad_id : id
issue_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsIssueItem {
id : id
--
position : integer
image : boolean
issue_id : id
item_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItem {
id : id
--
status : integer
type : integer
url : string
headline : string
story : string
image : string
object_id : string
feed_only : boolean
pinned : boolean
published_at : utc_datetime
refreshed_at : utc_datetime
impression_count : integer
click_count : integer
message : string
author_id : id
logger_id : id
submitter_id : id
source_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItemComment {
id : id
--
content : string
approved : boolean
edited_at : utc_datetime
deleted_at : utc_datetime
item_id : id
author_id : id
parent_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsItemTopic {
id : id
--
position : integer
item_id : id
topic_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsQueue {
id : id
--
position : float
item_id : id
}
entity Changelog.NewsSource {
id : id
--
name : string
slug : string
website : string
twitter_handle : string
description : string
feed : string
regex : string
publication : boolean
icon : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.NewsSponsorship {
id : id
--
name : string
weeks : array
impression_count : integer
click_count : integer
sponsor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Person {
id : id
--
name : string
email : string
handle : string
github_handle : string
linkedin_handle : string
mastodon_handle : string
twitter_handle : string
slack_id : string
website : string
bio : string
location : string
auth_token : string
auth_token_expires_at : utc_datetime
joined_at : utc_datetime
signed_in_at : utc_datetime
approved : boolean
avatar : string
admin : boolean
host : boolean
editor : boolean
public_profile : boolean
settings : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Person.Settings {
subscribe_to_contributed_news : boolean
subscribe_to_participated_episodes : boolean
email_on_authored_news : boolean
email_on_submitted_news : boolean
email_on_comment_replies : boolean
email_on_comment_mentions : boolean
}
entity Changelog.Podcast {
id : id
--
name : string
slug : string
status : integer
welcome : string
description : string
extended_description : string
vanity_domain : string
keywords : string
mastodon_handle : string
twitter_handle : string
apple_url : string
spotify_url : string
riverside_url : string
chartable_id : string
schedule_note : string
download_count : float
reach_count : integer
recorded_live : boolean
partner : boolean
position : integer
subscribers : map
cover : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PodcastHost {
id : id
--
position : integer
retired : boolean
person_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PodcastTopic {
id : id
--
position : integer
podcast_id : id
topic_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Post {
id : id
--
title : string
subtitle : string
slug : string
guid : string
canonical_url : string
image : string
tldr : string
body : string
published : boolean
published_at : utc_datetime
author_id : id
editor_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.PostTopic {
id : id
--
position : integer
topic_id : id
post_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Sponsor {
id : id
--
name : string
description : string
website : string
github_handle : string
twitter_handle : string
avatar : string
color_logo : string
dark_logo : string
light_logo : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.SponsorRep {
id : id
--
sponsor_id : id
person_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Subscription {
id : id
--
unsubscribed_at : utc_datetime
context : string
episode_id : id
item_id : id
person_id : id
podcast_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Changelog.Topic {
id : id
--
name : string
slug : string
description : string
website : string
twitter_handle : string
icon : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
entity Oban.Job {
id : id
--
state : string
queue : string
worker : string
args : map
meta : map
tags : array
errors : array
attempt : integer
attempted_by : array
max_attempts : integer
priority : integer
attempted_at : utc_datetime_usec
cancelled_at : utc_datetime_usec
completed_at : utc_datetime_usec
discarded_at : utc_datetime_usec
inserted_at : utc_datetime_usec
scheduled_at : utc_datetime_usec
}
Changelog.EpisodeRequest ||--o| Changelog.Episode
Changelog.Episode ||--|{ Changelog.EpisodeChapter
Changelog.Episode ||--|{ Changelog.EpisodeGuest
Changelog.Episode ||--|{ Changelog.EpisodeHost
Changelog.Episode ||--|{ Changelog.EpisodeSponsor
Changelog.Episode ||--|{ Changelog.EpisodeStat
Changelog.Episode ||--|{ Changelog.EpisodeTopic
Changelog.Episode ||--|{ Changelog.Subscription
Changelog.NewsAd ||--|{ Changelog.NewsIssueAd
Changelog.NewsIssue ||--|{ Changelog.NewsIssueAd
Changelog.NewsIssue ||--|{ Changelog.NewsIssueItem
Changelog.NewsItemComment ||--|{ Changelog.NewsItemComment
Changelog.NewsItem ||--|{ Changelog.NewsIssueItem
Changelog.NewsItem ||--|{ Changelog.NewsItemComment
Changelog.NewsItem ||--|{ Changelog.NewsItemTopic
Changelog.NewsItem ||--o| Changelog.NewsQueue
Changelog.NewsItem ||--|{ Changelog.Subscription
Changelog.NewsSource ||--|{ Changelog.NewsItem
Changelog.NewsSponsorship ||--|{ Changelog.NewsAd
Changelog.Person ||--|{ Changelog.EpisodeGuest
Changelog.Person ||--|{ Changelog.EpisodeHost
Changelog.Person ||--|{ Changelog.EpisodeRequest
Changelog.Person ||--|{ Changelog.Feed
Changelog.Person ||--|{ Changelog.NewsItemComment
Changelog.Person ||--|{ Changelog.NewsItem
Changelog.Person ||--|{ Changelog.PodcastHost
Changelog.Person ||--|{ Changelog.Post
Changelog.Person ||--|{ Changelog.SponsorRep
Changelog.Person ||--|{ Changelog.Subscription
Changelog.Person ||--o| Changelog.Person.Settings
Changelog.Podcast ||--|{ Changelog.EpisodeRequest
Changelog.Podcast ||--|{ Changelog.EpisodeStat
Changelog.Podcast ||--|{ Changelog.Episode
Changelog.Podcast ||--|{ Changelog.PodcastHost
Changelog.Podcast ||--|{ Changelog.PodcastTopic
Changelog.Podcast ||--|{ Changelog.Subscription
Changelog.Post ||--|{ Changelog.PostTopic
Changelog.Sponsor ||--|{ Changelog.EpisodeSponsor
Changelog.Sponsor ||--|{ Changelog.NewsSponsorship
Changelog.Sponsor ||--|{ Changelog.SponsorRep
Changelog.Topic ||--|{ Changelog.EpisodeTopic
Changelog.Topic ||--|{ Changelog.NewsItemTopic
Changelog.Topic ||--|{ Changelog.PodcastTopic
Changelog.Topic ||--|{ Changelog.PostTopic
@enduml
================================================
FILE: examples/plantuml/hexpm/Contexts-as-clusters-no-fields.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
hide fields
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace Ecto.Migration #f0f8ff {
entity Ecto.Migration.SchemaMigration
}
namespace Hexpm.Accounts #8deeee {
entity Hexpm.Accounts.AuditLog
entity Hexpm.Accounts.Email
entity Hexpm.Accounts.Key
entity Hexpm.Accounts.Key.Use
entity Hexpm.Accounts.KeyPermission
entity Hexpm.Accounts.Organization
entity Hexpm.Accounts.OrganizationUser
entity Hexpm.Accounts.PasswordReset
entity Hexpm.Accounts.RecoveryCode
entity Hexpm.Accounts.Session
entity Hexpm.Accounts.TFA
entity Hexpm.Accounts.User
entity Hexpm.Accounts.UserHandles
}
namespace Hexpm.BlockAddress #fffafa {
entity Hexpm.BlockAddress.Entry
}
namespace Hexpm.Repository #eedfcc {
entity Hexpm.Repository.Download
entity Hexpm.Repository.Install
entity Hexpm.Repository.Package
entity Hexpm.Repository.PackageDependant
entity Hexpm.Repository.PackageDownload
entity Hexpm.Repository.PackageMetadata
entity Hexpm.Repository.PackageOwner
entity Hexpm.Repository.PackageReport
entity Hexpm.Repository.PackageReportComment
entity Hexpm.Repository.PackageReportRelease
entity Hexpm.Repository.Release
entity Hexpm.Repository.ReleaseDownload
entity Hexpm.Repository.ReleaseMetadata
entity Hexpm.Repository.ReleaseRetirement
entity Hexpm.Repository.Repository
entity Hexpm.Repository.Requirement
}
namespace Hexpm.ShortURLs #b4eeb4 {
entity Hexpm.ShortURLs.ShortURL
}
Hexpm.Accounts.TFA ||--|{ Hexpm.Accounts.RecoveryCode
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Key ||--o| Hexpm.Accounts.Key.Use
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.KeyPermission
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.Organization ||--o| Hexpm.Repository.Repository
Hexpm.Accounts.Organization ||--o| Hexpm.Accounts.User
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDependant
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageReport
Hexpm.Repository.Package ||--|{ Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Release
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Package ||--o| Hexpm.Repository.PackageMetadata
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Release ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseMetadata
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseRetirement
Hexpm.Repository.Repository ||--|{ Hexpm.Repository.Package
Hexpm.Accounts.User ||--o| Hexpm.Accounts.UserHandles
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Email
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReport
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.PasswordReset
Hexpm.Accounts.User ||--|{ Hexpm.Repository.Release
Hexpm.Accounts.User ||--o| Hexpm.Accounts.TFA
@enduml
================================================
FILE: examples/plantuml/hexpm/Contexts-as-clusters.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace Ecto.Migration #f0f8ff {
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
}
namespace Hexpm.Accounts #8deeee {
entity Hexpm.Accounts.AuditLog {
id : id
--
user_agent : string
remote_ip : string
action : string
params : map
user_id : id
organization_id : id
key_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.Email {
id : id
--
email : string
verified : boolean
primary : boolean
public : boolean
gravatar : boolean
verification_key : string
verification_expiry : utc_datetime_usec
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.Key {
id : id
--
name : string
secret_first : string
secret_second : string
public : boolean
revoke_at : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
last_use : map
user_id : id
organization_id : id
permissions : map
}
entity Hexpm.Accounts.Key.Use {
id : binary_id
--
used_at : utc_datetime_usec
user_agent : string
ip : string
}
entity Hexpm.Accounts.KeyPermission {
id : binary_id
--
domain : string
resource : string
}
entity Hexpm.Accounts.Organization {
id : id
--
name : string
billing_active : boolean
billing_override : boolean
trial_end : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.OrganizationUser {
id : id
--
role : string
organization_id : id
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.PasswordReset {
id : id
--
key : string
primary_email : string
user_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.RecoveryCode {
id : binary_id
--
code : string
used_at : utc_datetime_usec
}
entity Hexpm.Accounts.Session {
id : id
--
token : binary
data : map
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.TFA {
secret : string
tfa_enabled : boolean
app_enabled : boolean
recovery_codes : map
}
entity Hexpm.Accounts.User {
id : id
--
username : string
full_name : string
password : string
service : boolean
deactivated_at : utc_datetime_usec
role : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
handles : map
tfa : map
organization_id : id
}
entity Hexpm.Accounts.UserHandles {
id : binary_id
--
twitter : string
github : string
elixirforum : string
freenode : string
slack : string
}
}
namespace Hexpm.BlockAddress #fffafa {
entity Hexpm.BlockAddress.Entry {
id : id
--
ip : string
comment : string
}
}
namespace Hexpm.Repository #eedfcc {
entity Hexpm.Repository.Download {
id : id
--
package_id : id
release_id : id
downloads : integer
day : date
}
entity Hexpm.Repository.Install {
id : id
--
hex : string
elixirs : array
}
entity Hexpm.Repository.Package {
id : id
--
name : string
docs_updated_at : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
repository_id : id
meta : map
}
entity Hexpm.Repository.PackageDependant {
id : id
--
package_id : id
name : string
repo : string
}
entity Hexpm.Repository.PackageDownload {
package_id : id
view : string
downloads : integer
}
entity Hexpm.Repository.PackageMetadata {
id : binary_id
--
description : string
licenses : array
links : map
maintainers : array
extra : map
}
entity Hexpm.Repository.PackageOwner {
id : id
--
level : string
package_id : id
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.PackageReport {
id : id
--
state : string
description : string
author_id : id
package_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.PackageReportComment {
id : id
--
text : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
package_report_id : id
author_id : id
}
entity Hexpm.Repository.PackageReportRelease {
id : id
--
release_id : id
package_report_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.Release {
id : id
--
version : string
inner_checksum : binary
outer_checksum : binary
has_docs : boolean
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
package_id : id
publisher_id : id
meta : map
retirement : map
}
entity Hexpm.Repository.ReleaseDownload {
package_id : id
release_id : id
downloads : integer
}
entity Hexpm.Repository.ReleaseMetadata {
id : binary_id
--
app : string
build_tools : array
elixir : string
}
entity Hexpm.Repository.ReleaseRetirement {
id : binary_id
--
reason : string
message : string
}
entity Hexpm.Repository.Repository {
id : id
--
name : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
organization_id : id
}
entity Hexpm.Repository.Requirement {
id : id
--
app : string
requirement : string
optional : boolean
release_id : id
dependency_id : id
}
}
namespace Hexpm.ShortURLs #b4eeb4 {
entity Hexpm.ShortURLs.ShortURL {
id : id
--
url : string
short_code : string
inserted_at : utc_datetime_usec
}
}
Hexpm.Accounts.TFA ||--|{ Hexpm.Accounts.RecoveryCode
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Key ||--o| Hexpm.Accounts.Key.Use
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.KeyPermission
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.Organization ||--o| Hexpm.Repository.Repository
Hexpm.Accounts.Organization ||--o| Hexpm.Accounts.User
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDependant
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageReport
Hexpm.Repository.Package ||--|{ Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Release
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Package ||--o| Hexpm.Repository.PackageMetadata
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Release ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseMetadata
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseRetirement
Hexpm.Repository.Repository ||--|{ Hexpm.Repository.Package
Hexpm.Accounts.User ||--o| Hexpm.Accounts.UserHandles
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Email
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReport
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.PasswordReset
Hexpm.Accounts.User ||--|{ Hexpm.Repository.Release
Hexpm.Accounts.User ||--o| Hexpm.Accounts.TFA
@enduml
================================================
FILE: examples/plantuml/hexpm/Default.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
entity Hexpm.Accounts.AuditLog {
id : id
--
user_agent : string
remote_ip : string
action : string
params : map
user_id : id
organization_id : id
key_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.Email {
id : id
--
email : string
verified : boolean
primary : boolean
public : boolean
gravatar : boolean
verification_key : string
verification_expiry : utc_datetime_usec
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.Key {
id : id
--
name : string
secret_first : string
secret_second : string
public : boolean
revoke_at : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
last_use : map
user_id : id
organization_id : id
permissions : map
}
entity Hexpm.Accounts.Key.Use {
id : binary_id
--
used_at : utc_datetime_usec
user_agent : string
ip : string
}
entity Hexpm.Accounts.KeyPermission {
id : binary_id
--
domain : string
resource : string
}
entity Hexpm.Accounts.Organization {
id : id
--
name : string
billing_active : boolean
billing_override : boolean
trial_end : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.OrganizationUser {
id : id
--
role : string
organization_id : id
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.PasswordReset {
id : id
--
key : string
primary_email : string
user_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.RecoveryCode {
id : binary_id
--
code : string
used_at : utc_datetime_usec
}
entity Hexpm.Accounts.Session {
id : id
--
token : binary
data : map
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.TFA {
secret : string
tfa_enabled : boolean
app_enabled : boolean
recovery_codes : map
}
entity Hexpm.Accounts.User {
id : id
--
username : string
full_name : string
password : string
service : boolean
deactivated_at : utc_datetime_usec
role : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
handles : map
tfa : map
organization_id : id
}
entity Hexpm.Accounts.UserHandles {
id : binary_id
--
twitter : string
github : string
elixirforum : string
freenode : string
slack : string
}
entity Hexpm.BlockAddress.Entry {
id : id
--
ip : string
comment : string
}
entity Hexpm.Repository.Download {
id : id
--
package_id : id
release_id : id
downloads : integer
day : date
}
entity Hexpm.Repository.Install {
id : id
--
hex : string
elixirs : array
}
entity Hexpm.Repository.Package {
id : id
--
name : string
docs_updated_at : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
repository_id : id
meta : map
}
entity Hexpm.Repository.PackageDependant {
id : id
--
package_id : id
name : string
repo : string
}
entity Hexpm.Repository.PackageDownload {
package_id : id
view : string
downloads : integer
}
entity Hexpm.Repository.PackageMetadata {
id : binary_id
--
description : string
licenses : array
links : map
maintainers : array
extra : map
}
entity Hexpm.Repository.PackageOwner {
id : id
--
level : string
package_id : id
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.PackageReport {
id : id
--
state : string
description : string
author_id : id
package_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.PackageReportComment {
id : id
--
text : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
package_report_id : id
author_id : id
}
entity Hexpm.Repository.PackageReportRelease {
id : id
--
release_id : id
package_report_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Repository.Release {
id : id
--
version : string
inner_checksum : binary
outer_checksum : binary
has_docs : boolean
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
package_id : id
publisher_id : id
meta : map
retirement : map
}
entity Hexpm.Repository.ReleaseDownload {
package_id : id
release_id : id
downloads : integer
}
entity Hexpm.Repository.ReleaseMetadata {
id : binary_id
--
app : string
build_tools : array
elixir : string
}
entity Hexpm.Repository.ReleaseRetirement {
id : binary_id
--
reason : string
message : string
}
entity Hexpm.Repository.Repository {
id : id
--
name : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
organization_id : id
}
entity Hexpm.Repository.Requirement {
id : id
--
app : string
requirement : string
optional : boolean
release_id : id
dependency_id : id
}
entity Hexpm.ShortURLs.ShortURL {
id : id
--
url : string
short_code : string
inserted_at : utc_datetime_usec
}
Hexpm.Accounts.TFA ||--|{ Hexpm.Accounts.RecoveryCode
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Key ||--o| Hexpm.Accounts.Key.Use
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.KeyPermission
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.Organization ||--o| Hexpm.Repository.Repository
Hexpm.Accounts.Organization ||--o| Hexpm.Accounts.User
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Repository.PackageReport ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDependant
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Repository.Package ||--|{ Hexpm.Repository.PackageReport
Hexpm.Repository.Package ||--|{ Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Release
Hexpm.Repository.Package ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Package ||--o| Hexpm.Repository.PackageMetadata
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Download
Hexpm.Repository.Release ||--|{ Hexpm.Repository.PackageReportRelease
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseDownload
Hexpm.Repository.Release ||--|{ Hexpm.Repository.Requirement
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseMetadata
Hexpm.Repository.Release ||--o| Hexpm.Repository.ReleaseRetirement
Hexpm.Repository.Repository ||--|{ Hexpm.Repository.Package
Hexpm.Accounts.User ||--o| Hexpm.Accounts.UserHandles
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Email
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageOwner
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReportComment
Hexpm.Accounts.User ||--|{ Hexpm.Repository.PackageReport
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.PasswordReset
Hexpm.Accounts.User ||--|{ Hexpm.Repository.Release
Hexpm.Accounts.User ||--o| Hexpm.Accounts.TFA
@enduml
================================================
FILE: examples/plantuml/hexpm/Only-embedded-schemas.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
entity Hexpm.Accounts.Key.Use {
id : binary_id
--
used_at : utc_datetime_usec
user_agent : string
ip : string
}
entity Hexpm.Accounts.KeyPermission {
id : binary_id
--
domain : string
resource : string
}
entity Hexpm.Accounts.RecoveryCode {
id : binary_id
--
code : string
used_at : utc_datetime_usec
}
entity Hexpm.Accounts.TFA {
secret : string
tfa_enabled : boolean
app_enabled : boolean
recovery_codes : map
}
entity Hexpm.Accounts.UserHandles {
id : binary_id
--
twitter : string
github : string
elixirforum : string
freenode : string
slack : string
}
entity Hexpm.Repository.PackageMetadata {
id : binary_id
--
description : string
licenses : array
links : map
maintainers : array
extra : map
}
entity Hexpm.Repository.ReleaseMetadata {
id : binary_id
--
app : string
build_tools : array
elixir : string
}
entity Hexpm.Repository.ReleaseRetirement {
id : binary_id
--
reason : string
message : string
}
Hexpm.Accounts.TFA ||--|{ Hexpm.Accounts.RecoveryCode
@enduml
================================================
FILE: examples/plantuml/hexpm/Only-selected-cluster-Accounts-context.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace Hexpm.Accounts #8deeee {
entity Hexpm.Accounts.AuditLog {
id : id
--
user_agent : string
remote_ip : string
action : string
params : map
user_id : id
organization_id : id
key_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.Email {
id : id
--
email : string
verified : boolean
primary : boolean
public : boolean
gravatar : boolean
verification_key : string
verification_expiry : utc_datetime_usec
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.Key {
id : id
--
name : string
secret_first : string
secret_second : string
public : boolean
revoke_at : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
last_use : map
user_id : id
organization_id : id
permissions : map
}
entity Hexpm.Accounts.Key.Use {
id : binary_id
--
used_at : utc_datetime_usec
user_agent : string
ip : string
}
entity Hexpm.Accounts.KeyPermission {
id : binary_id
--
domain : string
resource : string
}
entity Hexpm.Accounts.Organization {
id : id
--
name : string
billing_active : boolean
billing_override : boolean
trial_end : utc_datetime_usec
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.OrganizationUser {
id : id
--
role : string
organization_id : id
user_id : id
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.PasswordReset {
id : id
--
key : string
primary_email : string
user_id : id
inserted_at : utc_datetime_usec
}
entity Hexpm.Accounts.RecoveryCode {
id : binary_id
--
code : string
used_at : utc_datetime_usec
}
entity Hexpm.Accounts.Session {
id : id
--
token : binary
data : map
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
}
entity Hexpm.Accounts.TFA {
secret : string
tfa_enabled : boolean
app_enabled : boolean
recovery_codes : map
}
entity Hexpm.Accounts.User {
id : id
--
username : string
full_name : string
password : string
service : boolean
deactivated_at : utc_datetime_usec
role : string
inserted_at : utc_datetime_usec
updated_at : utc_datetime_usec
handles : map
tfa : map
organization_id : id
}
entity Hexpm.Accounts.UserHandles {
id : binary_id
--
twitter : string
github : string
elixirforum : string
freenode : string
slack : string
}
}
Hexpm.Accounts.TFA ||--|{ Hexpm.Accounts.RecoveryCode
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Key ||--o| Hexpm.Accounts.Key.Use
Hexpm.Accounts.Key ||--|{ Hexpm.Accounts.KeyPermission
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.Organization ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.Organization ||--o| Hexpm.Accounts.User
Hexpm.Accounts.User ||--o| Hexpm.Accounts.UserHandles
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.AuditLog
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Email
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.Key
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.OrganizationUser
Hexpm.Accounts.User ||--|{ Hexpm.Accounts.PasswordReset
Hexpm.Accounts.User ||--o| Hexpm.Accounts.TFA
@enduml
================================================
FILE: examples/plantuml/plausible-analytics/Contexts-as-clusters-no-fields.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
hide fields
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace Ecto.Migration #f0f8ff {
entity Ecto.Migration.SchemaMigration
}
namespace FunWithFlags.Store #f0ffff {
entity FunWithFlags.Store.Persistent.Ecto.Record
}
namespace Oban #f0ffff {
entity Oban.Job
}
namespace Plausible #eee5de {
entity Plausible.ClickhouseEventV2
entity Plausible.ClickhouseSessionV2
entity Plausible.Funnel
entity Plausible.Goal
entity Plausible.Site
}
namespace Plausible.Auth #ffefd5 {
entity Plausible.Auth.ApiKey
entity Plausible.Auth.EmailActivationCode
entity Plausible.Auth.GracePeriod
entity Plausible.Auth.Invitation
entity Plausible.Auth.TOTP.RecoveryCode
entity Plausible.Auth.User
}
namespace Plausible.Billing #f0ffff {
entity Plausible.Billing.EnterprisePlan
entity Plausible.Billing.Plan
entity Plausible.Billing.Subscription
}
namespace Plausible.DataMigration #8deeee {
entity Plausible.DataMigration.NumericIDs.DomainsLookup
}
namespace Plausible.Funnel #fffafa {
entity Plausible.Funnel.Step
}
namespace Plausible.Imported #eedfcc {
entity Plausible.Imported.Browser
entity Plausible.Imported.Device
entity Plausible.Imported.EntryPage
entity Plausible.Imported.ExitPage
entity Plausible.Imported.Location
entity Plausible.Imported.OperatingSystem
entity Plausible.Imported.Page
entity Plausible.Imported.SiteImport
entity Plausible.Imported.Source
entity Plausible.Imported.Visitor
}
namespace Plausible.Ingestion #8deeee {
entity Plausible.Ingestion.Counters.Record
entity Plausible.Ingestion.Request
}
namespace Plausible.Plugins #eee5de {
entity Plausible.Plugins.API.Token
}
namespace Plausible.Shield #8deeee {
entity Plausible.Shield.CountryRule
entity Plausible.Shield.HostnameRule
entity Plausible.Shield.IPRule
entity Plausible.Shield.PageRule
}
namespace Plausible.Site #f0f8ff {
entity Plausible.Site.GoogleAuth
entity Plausible.Site.ImportedData
entity Plausible.Site.Membership
entity Plausible.Site.MonthlyReport
entity Plausible.Site.SharedLink
entity Plausible.Site.SpikeNotification
entity Plausible.Site.UserPreference
entity Plausible.Site.WeeklyReport
}
Plausible.Funnel ||--|{ Plausible.Funnel.Step
Plausible.Goal ||--|{ Plausible.Funnel.Step
Plausible.Site ||--|{ Plausible.Funnel
Plausible.Site ||--|{ Plausible.Goal
Plausible.Site ||--o| Plausible.Site.GoogleAuth
Plausible.Site ||--|{ Plausible.Auth.Invitation
Plausible.Site ||--o| Plausible.Site.MonthlyReport
Plausible.Site ||--|{ Plausible.Plugins.API.Token
Plausible.Site ||--|{ Plausible.Site.SharedLink
Plausible.Site ||--|{ Plausible.Shield.CountryRule
Plausible.Site ||--|{ Plausible.Shield.HostnameRule
Plausible.Site ||--|{ Plausible.Shield.IPRule
Plausible.Site ||--|{ Plausible.Shield.PageRule
Plausible.Site ||--|{ Plausible.Imported.SiteImport
Plausible.Site ||--o| Plausible.Site.Membership
Plausible.Site ||--|{ Plausible.Site.UserPreference
Plausible.Site ||--o| Plausible.Site.SpikeNotification
Plausible.Site ||--o| Plausible.Site.WeeklyReport
Plausible.Site ||--o| Plausible.Site.ImportedData
Plausible.Auth.User ||--o| Plausible.Auth.GracePeriod
Plausible.Auth.User ||--|{ Plausible.Auth.ApiKey
Plausible.Auth.User ||--|{ Plausible.Auth.EmailActivationCode
Plausible.Auth.User ||--o| Plausible.Billing.EnterprisePlan
Plausible.Auth.User ||--o| Plausible.Site.GoogleAuth
Plausible.Auth.User ||--|{ Plausible.Auth.Invitation
Plausible.Auth.User ||--|{ Plausible.Imported.SiteImport
Plausible.Auth.User ||--|{ Plausible.Site.Membership
Plausible.Auth.User ||--|{ Plausible.Site.UserPreference
Plausible.Auth.User ||--o| Plausible.Billing.Subscription
Plausible.Auth.User ||--|{ Plausible.Auth.TOTP.RecoveryCode
@enduml
================================================
FILE: examples/plantuml/plausible-analytics/Contexts-as-clusters.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
namespace Ecto.Migration #f0f8ff {
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
}
namespace FunWithFlags.Store #f0ffff {
entity FunWithFlags.Store.Persistent.Ecto.Record {
id : id
--
flag_name : string
gate_type : string
target : string
enabled : boolean
}
}
namespace Oban #f0ffff {
entity Oban.Job {
id : id
--
state : string
queue : string
worker : string
args : map
meta : map
tags : array
errors : array
attempt : integer
attempted_by : array
max_attempts : integer
priority : integer
attempted_at : utc_datetime_usec
cancelled_at : utc_datetime_usec
completed_at : utc_datetime_usec
discarded_at : utc_datetime_usec
inserted_at : utc_datetime_usec
scheduled_at : utc_datetime_usec
}
}
namespace Plausible #eee5de {
entity Plausible.ClickhouseEventV2 {
name : unknown
site_id : unknown
hostname : string
pathname : string
user_id : unknown
session_id : unknown
timestamp : naive_datetime
meta.key : array
meta.value : array
revenue_source_amount : unknown
revenue_source_currency : unknown
revenue_reporting_amount : unknown
revenue_reporting_currency : unknown
referrer : string
referrer_source : string
utm_medium : string
utm_source : string
utm_campaign : string
utm_content : string
utm_term : string
country_code : unknown
subdivision1_code : unknown
subdivision2_code : unknown
city_geoname_id : unknown
screen_size : unknown
operating_system : unknown
operating_system_version : unknown
browser : unknown
browser_version : unknown
}
entity Plausible.ClickhouseSessionV2 {
hostname : string
site_id : unknown
user_id : unknown
session_id : unknown
start : naive_datetime
duration : unknown
is_bounce : unknown
entry_page : string
exit_page : string
exit_page_hostname : string
pageviews : unknown
events : unknown
sign : unknown
entry_meta.key : array
entry_meta.value : array
utm_medium : string
utm_source : string
utm_campaign : string
utm_content : string
utm_term : string
referrer : string
referrer_source : string
country_code : unknown
subdivision1_code : unknown
subdivision2_code : unknown
city_geoname_id : unknown
screen_size : unknown
operating_system : unknown
operating_system_version : unknown
browser : unknown
browser_version : unknown
timestamp : naive_datetime
transferred_from : string
}
entity Plausible.Funnel {
id : id
--
name : string
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Goal {
id : id
--
event_name : string
page_path : string
currency : enum(XBD,BYN,HKD,XOF,SOS,ARS,EGP,XDR,GMD,...)
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site {
id : id
--
domain : string
timezone : string
public : boolean
locked : boolean
stats_start_date : date
native_stats_start_at : naive_datetime
allowed_event_props : array
conversions_enabled : boolean
props_enabled : boolean
funnels_enabled : boolean
ingest_rate_limit_scale_seconds : integer
ingest_rate_limit_threshold : integer
domain_changed_from : string
domain_changed_at : naive_datetime
imported_data : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace Plausible.Auth #ffefd5 {
entity Plausible.Auth.ApiKey {
id : id
--
name : string
scopes : array
hourly_request_limit : integer
key_hash : string
key_prefix : string
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Auth.EmailActivationCode {
id : id
--
code : string
issued_at : naive_datetime
user_id : id
}
entity Plausible.Auth.GracePeriod {
id : binary_id
--
end_date : date
is_over : boolean
manual_lock : boolean
}
entity Plausible.Auth.Invitation {
id : id
--
invitation_id : string
email : string
role : enum(owner,admin,viewer)
inviter_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Auth.TOTP.RecoveryCode {
id : id
--
code_digest : string
user_id : id
inserted_at : naive_datetime
}
entity Plausible.Auth.User {
id : id
--
email : string
password_hash : string
name : string
last_seen : naive_datetime
trial_expiry_date : date
theme : enum(system,light,dark)
email_verified : boolean
previous_email : string
accept_traffic_until : date
allow_next_upgrade_override : boolean
totp_enabled : boolean
totp_secret : binary
totp_token : string
totp_last_used_at : naive_datetime
grace_period : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace Plausible.Billing #f0ffff {
entity Plausible.Billing.EnterprisePlan {
id : id
--
paddle_plan_id : string
billing_interval : enum(yearly,monthly)
monthly_pageview_limit : integer
site_limit : integer
team_member_limit : integer
features : array
hourly_api_request_limit : integer
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Billing.Plan {
id : binary_id
--
generation : integer
kind : enum(growth,business)
features : array
monthly_pageview_limit : integer
site_limit : integer
team_member_limit : integer
volume : string
data_retention_in_years : integer
monthly_cost : string
monthly_product_id : string
yearly_cost : string
yearly_product_id : string
}
entity Plausible.Billing.Subscription {
id : id
--
paddle_subscription_id : string
paddle_plan_id : string
update_url : string
cancel_url : string
status : enum(active,deleted,past_due,paused)
next_bill_amount : string
next_bill_date : date
last_bill_date : date
currency_code : string
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace Plausible.DataMigration #8deeee {
entity Plausible.DataMigration.NumericIDs.DomainsLookup {
site_id : unknown
domain : string
}
}
namespace Plausible.Funnel #fffafa {
entity Plausible.Funnel.Step {
id : id
--
step_order : integer
funnel_id : id
goal_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace Plausible.Imported #eedfcc {
entity Plausible.Imported.Browser {
site_id : unknown
import_id : unknown
date : date
browser : string
browser_version : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Device {
site_id : unknown
import_id : unknown
date : date
device : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.EntryPage {
site_id : unknown
import_id : unknown
date : date
entry_page : string
visitors : unknown
entrances : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.ExitPage {
site_id : unknown
import_id : unknown
date : date
exit_page : string
exits : unknown
visitors : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Location {
site_id : unknown
import_id : unknown
date : date
country : string
region : string
city : unknown
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.OperatingSystem {
site_id : unknown
import_id : unknown
date : date
operating_system : string
operating_system_version : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Page {
site_id : unknown
import_id : unknown
date : date
hostname : string
page : string
visits : unknown
visitors : unknown
active_visitors : unknown
pageviews : unknown
exits : unknown
time_on_page : unknown
}
entity Plausible.Imported.SiteImport {
id : id
--
start_date : date
end_date : date
label : string
source : enum(noop,csv,universal_analytics,google_analytics_4)
status : enum(pending,failed,completed,importing)
legacy : boolean
site_id : id
imported_by_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Imported.Source {
site_id : unknown
import_id : unknown
date : date
source : string
referrer : string
utm_source : string
utm_medium : string
utm_campaign : string
utm_content : string
utm_term : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Visitor {
site_id : unknown
import_id : unknown
date : date
visitors : unknown
pageviews : unknown
bounces : unknown
visits : unknown
visit_duration : unknown
}
}
namespace Plausible.Ingestion #8deeee {
entity Plausible.Ingestion.Counters.Record {
event_timebucket : utc_datetime
site_id : unknown
domain : unknown
metric : unknown
value : unknown
}
entity Plausible.Ingestion.Request {
remote_ip : string
user_agent : string
event_name : string
uri : map
hostname : string
referrer : string
domains : array
ip_classification : string
hash_mode : integer
pathname : string
props : map
revenue_source : map
query_params : map
timestamp : naive_datetime
}
}
namespace Plausible.Plugins #eee5de {
entity Plausible.Plugins.API.Token {
id : binary_id
--
inserted_at : naive_datetime
updated_at : naive_datetime
token_hash : binary
description : string
hint : string
last_used_at : naive_datetime
site_id : id
}
}
namespace Plausible.Shield #8deeee {
entity Plausible.Shield.CountryRule {
id : binary_id
--
site_id : id
country_code : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.HostnameRule {
id : binary_id
--
site_id : id
hostname : string
hostname_pattern : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.IPRule {
id : binary_id
--
site_id : id
inet : inet
action : enum(allow,deny)
description : string
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.PageRule {
id : binary_id
--
site_id : id
page_path : string
page_path_pattern : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
namespace Plausible.Site #f0f8ff {
entity Plausible.Site.GoogleAuth {
id : id
--
email : string
property : string
refresh_token : string
access_token : string
expires : naive_datetime
user_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.ImportedData {
id : binary_id
--
start_date : date
end_date : date
source : string
status : string
}
entity Plausible.Site.Membership {
id : id
--
role : enum(owner,admin,viewer)
site_id : id
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.MonthlyReport {
id : id
--
recipients : array
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.SharedLink {
id : id
--
site_id : id
name : string
slug : string
password_hash : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.SpikeNotification {
id : id
--
recipients : array
threshold : integer
last_sent : naive_datetime
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.UserPreference {
id : id
--
pinned_at : naive_datetime
user_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.WeeklyReport {
id : id
--
recipients : array
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
}
Plausible.Funnel ||--|{ Plausible.Funnel.Step
Plausible.Goal ||--|{ Plausible.Funnel.Step
Plausible.Site ||--|{ Plausible.Funnel
Plausible.Site ||--|{ Plausible.Goal
Plausible.Site ||--o| Plausible.Site.GoogleAuth
Plausible.Site ||--|{ Plausible.Auth.Invitation
Plausible.Site ||--o| Plausible.Site.MonthlyReport
Plausible.Site ||--|{ Plausible.Plugins.API.Token
Plausible.Site ||--|{ Plausible.Site.SharedLink
Plausible.Site ||--|{ Plausible.Shield.CountryRule
Plausible.Site ||--|{ Plausible.Shield.HostnameRule
Plausible.Site ||--|{ Plausible.Shield.IPRule
Plausible.Site ||--|{ Plausible.Shield.PageRule
Plausible.Site ||--|{ Plausible.Imported.SiteImport
Plausible.Site ||--o| Plausible.Site.Membership
Plausible.Site ||--|{ Plausible.Site.UserPreference
Plausible.Site ||--o| Plausible.Site.SpikeNotification
Plausible.Site ||--o| Plausible.Site.WeeklyReport
Plausible.Site ||--o| Plausible.Site.ImportedData
Plausible.Auth.User ||--o| Plausible.Auth.GracePeriod
Plausible.Auth.User ||--|{ Plausible.Auth.ApiKey
Plausible.Auth.User ||--|{ Plausible.Auth.EmailActivationCode
Plausible.Auth.User ||--o| Plausible.Billing.EnterprisePlan
Plausible.Auth.User ||--o| Plausible.Site.GoogleAuth
Plausible.Auth.User ||--|{ Plausible.Auth.Invitation
Plausible.Auth.User ||--|{ Plausible.Imported.SiteImport
Plausible.Auth.User ||--|{ Plausible.Site.Membership
Plausible.Auth.User ||--|{ Plausible.Site.UserPreference
Plausible.Auth.User ||--o| Plausible.Billing.Subscription
Plausible.Auth.User ||--|{ Plausible.Auth.TOTP.RecoveryCode
@enduml
================================================
FILE: examples/plantuml/plausible-analytics/Default.puml
================================================
@startuml
set namespaceSeparator none
hide circle
hide methods
skinparam linetype ortho
skinparam defaultFontName Roboto Mono
skinparam shadowing false
entity Ecto.Migration.SchemaMigration {
version : integer
--
inserted_at : naive_datetime
}
entity FunWithFlags.Store.Persistent.Ecto.Record {
id : id
--
flag_name : string
gate_type : string
target : string
enabled : boolean
}
entity Oban.Job {
id : id
--
state : string
queue : string
worker : string
args : map
meta : map
tags : array
errors : array
attempt : integer
attempted_by : array
max_attempts : integer
priority : integer
attempted_at : utc_datetime_usec
cancelled_at : utc_datetime_usec
completed_at : utc_datetime_usec
discarded_at : utc_datetime_usec
inserted_at : utc_datetime_usec
scheduled_at : utc_datetime_usec
}
entity Plausible.Auth.ApiKey {
id : id
--
name : string
scopes : array
hourly_request_limit : integer
key_hash : string
key_prefix : string
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Auth.EmailActivationCode {
id : id
--
code : string
issued_at : naive_datetime
user_id : id
}
entity Plausible.Auth.GracePeriod {
id : binary_id
--
end_date : date
is_over : boolean
manual_lock : boolean
}
entity Plausible.Auth.Invitation {
id : id
--
invitation_id : string
email : string
role : enum(owner,admin,viewer)
inviter_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Auth.TOTP.RecoveryCode {
id : id
--
code_digest : string
user_id : id
inserted_at : naive_datetime
}
entity Plausible.Auth.User {
id : id
--
email : string
password_hash : string
name : string
last_seen : naive_datetime
trial_expiry_date : date
theme : enum(system,light,dark)
email_verified : boolean
previous_email : string
accept_traffic_until : date
allow_next_upgrade_override : boolean
totp_enabled : boolean
totp_secret : binary
totp_token : string
totp_last_used_at : naive_datetime
grace_period : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Billing.EnterprisePlan {
id : id
--
paddle_plan_id : string
billing_interval : enum(yearly,monthly)
monthly_pageview_limit : integer
site_limit : integer
team_member_limit : integer
features : array
hourly_api_request_limit : integer
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Billing.Plan {
id : binary_id
--
generation : integer
kind : enum(growth,business)
features : array
monthly_pageview_limit : integer
site_limit : integer
team_member_limit : integer
volume : string
data_retention_in_years : integer
monthly_cost : string
monthly_product_id : string
yearly_cost : string
yearly_product_id : string
}
entity Plausible.Billing.Subscription {
id : id
--
paddle_subscription_id : string
paddle_plan_id : string
update_url : string
cancel_url : string
status : enum(active,deleted,past_due,paused)
next_bill_amount : string
next_bill_date : date
last_bill_date : date
currency_code : string
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.ClickhouseEventV2 {
name : unknown
site_id : unknown
hostname : string
pathname : string
user_id : unknown
session_id : unknown
timestamp : naive_datetime
meta.key : array
meta.value : array
revenue_source_amount : unknown
revenue_source_currency : unknown
revenue_reporting_amount : unknown
revenue_reporting_currency : unknown
referrer : string
referrer_source : string
utm_medium : string
utm_source : string
utm_campaign : string
utm_content : string
utm_term : string
country_code : unknown
subdivision1_code : unknown
subdivision2_code : unknown
city_geoname_id : unknown
screen_size : unknown
operating_system : unknown
operating_system_version : unknown
browser : unknown
browser_version : unknown
}
entity Plausible.ClickhouseSessionV2 {
hostname : string
site_id : unknown
user_id : unknown
session_id : unknown
start : naive_datetime
duration : unknown
is_bounce : unknown
entry_page : string
exit_page : string
exit_page_hostname : string
pageviews : unknown
events : unknown
sign : unknown
entry_meta.key : array
entry_meta.value : array
utm_medium : string
utm_source : string
utm_campaign : string
utm_content : string
utm_term : string
referrer : string
referrer_source : string
country_code : unknown
subdivision1_code : unknown
subdivision2_code : unknown
city_geoname_id : unknown
screen_size : unknown
operating_system : unknown
operating_system_version : unknown
browser : unknown
browser_version : unknown
timestamp : naive_datetime
transferred_from : string
}
entity Plausible.DataMigration.NumericIDs.DomainsLookup {
site_id : unknown
domain : string
}
entity Plausible.Funnel {
id : id
--
name : string
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Funnel.Step {
id : id
--
step_order : integer
funnel_id : id
goal_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Goal {
id : id
--
event_name : string
page_path : string
currency : enum(KMF,AUD,SAR,BWP,BBD,EGP,YER,CDF,IQD,...)
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Imported.Browser {
site_id : unknown
import_id : unknown
date : date
browser : string
browser_version : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Device {
site_id : unknown
import_id : unknown
date : date
device : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.EntryPage {
site_id : unknown
import_id : unknown
date : date
entry_page : string
visitors : unknown
entrances : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.ExitPage {
site_id : unknown
import_id : unknown
date : date
exit_page : string
exits : unknown
visitors : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Location {
site_id : unknown
import_id : unknown
date : date
country : string
region : string
city : unknown
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.OperatingSystem {
site_id : unknown
import_id : unknown
date : date
operating_system : string
operating_system_version : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Page {
site_id : unknown
import_id : unknown
date : date
hostname : string
page : string
visits : unknown
visitors : unknown
active_visitors : unknown
pageviews : unknown
exits : unknown
time_on_page : unknown
}
entity Plausible.Imported.SiteImport {
id : id
--
start_date : date
end_date : date
label : string
source : enum(noop,csv,universal_analytics,google_analytics_4)
status : enum(pending,failed,completed,importing)
legacy : boolean
site_id : id
imported_by_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Imported.Source {
site_id : unknown
import_id : unknown
date : date
source : string
referrer : string
utm_source : string
utm_medium : string
utm_campaign : string
utm_content : string
utm_term : string
visitors : unknown
visits : unknown
visit_duration : unknown
pageviews : unknown
bounces : unknown
}
entity Plausible.Imported.Visitor {
site_id : unknown
import_id : unknown
date : date
visitors : unknown
pageviews : unknown
bounces : unknown
visits : unknown
visit_duration : unknown
}
entity Plausible.Ingestion.Counters.Record {
event_timebucket : utc_datetime
site_id : unknown
domain : unknown
metric : unknown
value : unknown
}
entity Plausible.Ingestion.Request {
remote_ip : string
user_agent : string
event_name : string
uri : map
hostname : string
referrer : string
domains : array
ip_classification : string
hash_mode : integer
pathname : string
props : map
revenue_source : map
query_params : map
timestamp : naive_datetime
}
entity Plausible.Plugins.API.Token {
id : binary_id
--
inserted_at : naive_datetime
updated_at : naive_datetime
token_hash : binary
description : string
hint : string
last_used_at : naive_datetime
site_id : id
}
entity Plausible.Shield.CountryRule {
id : binary_id
--
site_id : id
country_code : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.HostnameRule {
id : binary_id
--
site_id : id
hostname : string
hostname_pattern : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.IPRule {
id : binary_id
--
site_id : id
inet : inet
action : enum(allow,deny)
description : string
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Shield.PageRule {
id : binary_id
--
site_id : id
page_path : string
page_path_pattern : string
action : enum(allow,deny)
added_by : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site {
id : id
--
domain : string
timezone : string
public : boolean
locked : boolean
stats_start_date : date
native_stats_start_at : naive_datetime
allowed_event_props : array
conversions_enabled : boolean
props_enabled : boolean
funnels_enabled : boolean
ingest_rate_limit_scale_seconds : integer
ingest_rate_limit_threshold : integer
domain_changed_from : string
domain_changed_at : naive_datetime
imported_data : map
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.GoogleAuth {
id : id
--
email : string
property : string
refresh_token : string
access_token : string
expires : naive_datetime
user_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.ImportedData {
id : binary_id
--
start_date : date
end_date : date
source : string
status : string
}
entity Plausible.Site.Membership {
id : id
--
role : enum(owner,admin,viewer)
site_id : id
user_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.MonthlyReport {
id : id
--
recipients : array
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.SharedLink {
id : id
--
site_id : id
name : string
slug : string
password_hash : string
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.SpikeNotification {
id : id
--
recipients : array
threshold : integer
last_sent : naive_datetime
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.UserPreference {
id : id
--
pinned_at : naive_datetime
user_id : id
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
entity Plausible.Site.WeeklyReport {
id : id
--
recipients : array
site_id : id
inserted_at : naive_datetime
updated_at : naive_datetime
}
Plausible.Funnel ||--|{ Plausible.Funnel.Step
Plausible.Goal ||--|{ Plausible.Funnel.Step
Plausible.Site ||--|{ Plausible.Funnel
Plausible.Site ||--|{ Plausible.Goal
Plausible.Site ||--o| Plausible.Site.GoogleAuth
Plausible.Site ||--|{ Plausible.Auth.Invitation
Plausible.Site ||--o| Plausible.Site.MonthlyReport
Plausible.Site ||--|{ Plausible.Plugins.API.Token
Plausible.Site ||--|{ Plausible.Site.SharedLink
Plausible.Site ||--|{ Plausible.Shield.CountryRule
Plausible.Site ||--|{ Plausible.Shield.HostnameRule
Plausible.Site ||--|{ Plausible.Shield.IPRule
Plausible.Site ||--|{ Plausible.Shield.PageRule
Plausible.Site ||--|{ Plausible.Imported.SiteImport
Plausible.Site ||--o| Plausible.Site.Membership
Plausible.Site ||--|{ Plausible.Site.UserPreference
Plausible.Site ||--o| Plausible.Site.SpikeNotification
Plausible.Site ||--o| Plausible.Site.WeeklyReport
Plausible.Site ||--o| Plausible.Site.ImportedData
Plausible.Auth.User ||--o| Plausible.Auth.GracePeriod
Plausible.Auth.User ||--|{ Plausible.Auth.ApiKey
Plausible.Auth.User ||--|{ Plausible.Auth.EmailActivationCode
Plausible.Auth.User ||--o| Plausible.Billing.EnterprisePlan
Plausible.Auth.User ||--o| Plausible.Site.GoogleAuth
Plausible.Auth.User ||--|{ Plausible.Auth.Invitation
Plausible.Auth.User ||--|{ Plausible.Imported.SiteImport
Plausible.Auth.User ||--|{ Plausible.Site.Membership
Plausible.Auth.User ||--|{ Plausible.Site.UserPreference
Plausible.Auth.User ||--o| Plausible.Billing.Subscription
Plausible.Auth.User ||--|{ Plausible.Auth.TOTP.RecoveryCode
@enduml
================================================
FILE: examples/quick_dbd/changelog.com/Default.qdbd
================================================
episodes
---
id integer PK
slug varchar
guid varchar
title varchar
subtitle varchar
type integer
featured boolean
highlight varchar
subhighlight varchar
summary varchar
notes varchar
doc_url varchar
socialize_url varchar
published boolean
published_at timestamp
recorded_at timestamp
recorded_live boolean
youtube_id varchar
cover varchar
audio_file varchar
audio_bytes integer
audio_duration integer
audio_chapters jsonb
plusplus_file varchar
plusplus_bytes integer
plusplus_duration integer
plusplus_chapters jsonb
download_count float
import_count float
reach_count integer
email_subject varchar
email_teaser varchar
email_content varchar
email_sends integer
email_opens integer
transcript array
podcast_id integer FK >- podcasts.id
request_id integer FK - episode_requests.id
inserted_at timestamp
updated_at timestamp
episode_guests
---
id integer PK
position integer
thanks boolean
discount_code varchar
episode_id integer FK >- episodes.id
person_id integer FK >- people.id
inserted_at timestamp
updated_at timestamp
episode_hosts
---
id integer PK
position integer
person_id integer FK >- people.id
episode_id integer FK >- episodes.id
inserted_at timestamp
updated_at timestamp
episode_requests
---
id integer PK
status integer
hosts varchar
guests varchar
topics varchar
pitch varchar
pronunciation varchar
message varchar
podcast_id integer FK >- podcasts.id
submitter_id integer FK >- people.id
inserted_at timestamp
updated_at timestamp
episode_sponsors
---
id integer PK
position integer
title varchar
link_url varchar
description varchar
starts_at float
ends_at float
episode_id integer FK >- episodes.id
sponsor_id integer FK >- sponsors.id
inserted_at timestamp
updated_at timestamp
episode_stats
---
id integer PK
date date
episode_bytes integer
total_bytes integer
downloads float
uniques integer
demographics jsonb
episode_id integer FK >- episodes.id
podcast_id integer FK >- podcasts.id
inserted_at timestamp
updated_at timestamp
episode_topics
---
id integer PK
position integer
topic_id integer FK >- topics.id
episode_id integer FK >- episodes.id
inserted_at timestamp
updated_at timestamp
feeds
---
id integer PK
name varchar
slug varchar
description varchar
title_format varchar
plusplus boolean
autosub boolean
starts_at timestamp
cover varchar
podcast_ids array
person_ids array
owner_id integer FK >- people.id
inserted_at timestamp
updated_at timestamp
news_ads
---
id integer PK
url varchar
headline varchar
story varchar
image varchar
active boolean
newsletter boolean
impression_count integer
click_count integer
sponsorship_id integer FK >- news_sponsorships.id
inserted_at timestamp
updated_at timestamp
news_issues
---
id integer PK
slug varchar
note varchar
teaser varchar
published boolean
published_at timestamp
inserted_at timestamp
updated_at timestamp
news_issue_ads
---
id integer PK
position integer
image boolean
ad_id integer FK >- news_ads.id
issue_id integer FK >- news_issues.id
inserted_at timestamp
updated_at timestamp
news_issue_items
---
id integer PK
position integer
image boolean
issue_id integer FK >- news_issues.id
item_id integer FK >- news_items.id
inserted_at timestamp
updated_at timestamp
news_items
---
id integer PK
status integer
type integer
url varchar
headline varchar
story varchar
image varchar
object_id varchar
feed_only boolean
pinned boolean
published_at timestamp
refreshed_at timestamp
impression_count integer
click_count integer
message varchar
author_id integer FK >- people.id
logger_id integer FK >- people.id
submitter_id integer FK >- people.id
source_id integer FK >- news_sources.id
inserted_at timestamp
updated_at timestamp
news_item_comments
---
id integer PK
content varchar
approved boolean
edited_at timestamp
deleted_at timestamp
item_id integer FK >- news_items.id
author_id integer FK >- people.id
parent_id integer FK >- news_item_comments.id
inserted_at timestamp
updated_at timestamp
news_item_topics
---
id integer PK
position integer
item_id integer FK >- news_items.id
topic_id integer FK >- topics.id
inserted_at timestamp
updated_at timestamp
news_queue
---
id integer PK
position float
item_id integer FK - news_items.id
news_sources
---
id integer PK
name varchar
slug varchar
website varchar
twitter_handle varchar
description varchar
feed varchar
regex varchar
publication boolean
icon varchar
inserted_at timestamp
updated_at timestamp
news_sponsorships
---
id integer PK
name varchar
weeks array
impression_count integer
click_count integer
sponsor_id integer FK >- sponsors.id
inserted_at timestamp
updated_at timestamp
people
---
id integer PK
name varchar
email varchar
handle varchar
github_handle varchar
linkedin_handle varchar
mastodon_handle varchar
twitter_handle varchar
slack_id varchar
website varchar
bio varchar
location varchar
auth_token varchar
auth_token_expires_at timestamp
joined_at timestamp
signed_in_at timestamp
approved boolean
avatar varchar
admin boolean
host boolean
editor boolean
public_profile boolean
settings jsonb
inserted_at timestamp
updated_at timestamp
podcasts
---
id integer PK
name varchar
slug varchar
status integer
welcome varchar
description varchar
extended_description varchar
vanity_domain varchar
keywords varchar
mastodon_handle varchar
twitter_handle varchar
apple_url varchar
spotify_url varchar
riverside_url varchar
chartable_id varchar
schedule_note varchar
download_count float
reach_count integer
recorded_live boolean
partner boolean
position integer
subscribers jsonb
cover varchar
inserted_at timestamp
updated_at timestamp
podcast_hosts
---
id integer PK
position integer
retired boolean
person_id integer FK >- people.id
podcast_id integer FK >- podcasts.id
inserted_at timestamp
updated_at timestamp
podcast_topics
---
id integer PK
position integer
podcast_id integer FK >- podcasts.id
topic_id integer FK >- topics.id
inserted_at timestamp
updated_at timestamp
posts
---
id integer PK
title varchar
subtitle varchar
slug varchar
guid varchar
canonical_url varchar
image varchar
tldr varchar
body varchar
published boolean
published_at timestamp
author_id integer FK >- people.id
editor_id integer FK >- people.id
inserted_at timestamp
updated_at timestamp
post_topics
---
id integer PK
position integer
topic_id integer FK >- topics.id
post_id integer FK >- posts.id
inserted_at timestamp
updated_at timestamp
sponsors
---
id integer PK
name varchar
description varchar
website varchar
github_handle varchar
twitter_handle varchar
avatar varchar
color_logo varchar
dark_logo varchar
light_logo varchar
inserted_at timestamp
updated_at timestamp
sponsor_reps
---
id integer PK
sponsor_id integer FK >- sponsors.id
person_id integer FK >- people.id
inserted_at timestamp
updated_at timestamp
subscriptions
---
id integer PK
unsubscribed_at timestamp
context varchar
episode_id integer FK >- episodes.id
item_id integer FK >- news_items.id
person_id integer FK >- people.id
podcast_id integer FK >- podcasts.id
inserted_at timestamp
updated_at timestamp
topics
---
id integer PK
name varchar
slug varchar
description varchar
website varchar
twitter_handle varchar
icon varchar
inserted_at timestamp
updated_at timestamp
schema_migrations
---
version integer PK
inserted_at timestamp
oban_jobs
---
id integer PK
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
================================================
FILE: examples/quick_dbd/hexpm/Default.qdbd
================================================
schema_migrations
---
version integer PK
inserted_at timestamp
audit_logs
---
id integer PK
user_agent varchar
remote_ip varchar
action varchar
params jsonb
user_id integer FK >- users.id
organization_id integer FK >- organizations.id
key_id integer FK >- keys.id
inserted_at timestamp
emails
---
id integer PK
email varchar
verified boolean
primary boolean
public boolean
gravatar boolean
verification_key varchar
verification_expiry timestamp
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
keys
---
id integer PK
name varchar
secret_first varchar
secret_second varchar
public boolean
revoke_at timestamp
inserted_at timestamp
updated_at timestamp
last_use jsonb
user_id integer FK >- users.id
organization_id integer FK >- organizations.id
permissions jsonb
organizations
---
id integer PK
name varchar
billing_active boolean
billing_override boolean
trial_end timestamp
inserted_at timestamp
updated_at timestamp
organization_users
---
id integer PK
role varchar
organization_id integer FK >- organizations.id
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
password_resets
---
id integer PK
key varchar
primary_email varchar
user_id integer FK >- users.id
inserted_at timestamp
sessions
---
id integer PK
token bytea
data jsonb
inserted_at timestamp
updated_at timestamp
users
---
id integer PK
username varchar
full_name varchar
password varchar
service boolean
deactivated_at timestamp
role varchar
inserted_at timestamp
updated_at timestamp
handles jsonb
tfa jsonb
organization_id integer FK - organizations.id
blocked_addresses
---
id integer PK
ip varchar
comment varchar
downloads
---
id integer PK
package_id integer FK >- packages.id
release_id integer FK >- releases.id
downloads integer
day date
installs
---
id integer PK
hex varchar
elixirs array
packages
---
id integer PK
name varchar
docs_updated_at timestamp
inserted_at timestamp
updated_at timestamp
repository_id integer FK >- repositories.id
meta jsonb
package_dependants
---
id integer PK
package_id integer FK >- packages.id
name varchar
repo varchar
package_downloads
---
package_id integer FK >- packages.id
view varchar
downloads integer
package_owners
---
id integer PK
level varchar
package_id integer FK >- packages.id
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
package_reports
---
id integer PK
state varchar
description varchar
author_id integer FK >- users.id
package_id integer FK >- packages.id
inserted_at timestamp
updated_at timestamp
package_report_comments
---
id integer PK
text varchar
inserted_at timestamp
updated_at timestamp
package_report_id integer FK >- package_reports.id
author_id integer FK >- users.id
package_report_releases
---
id integer PK
release_id integer FK >- releases.id
package_report_id integer FK >- package_reports.id
inserted_at timestamp
updated_at timestamp
releases
---
id integer PK
version varchar
inner_checksum bytea
outer_checksum bytea
has_docs boolean
inserted_at timestamp
updated_at timestamp
package_id integer FK >- packages.id
publisher_id integer FK >- users.id
meta jsonb
retirement jsonb
release_downloads
---
package_id integer FK >- packages.id
release_id integer FK - releases.id
downloads integer
repositories
---
id integer PK
name varchar
inserted_at timestamp
updated_at timestamp
organization_id integer FK - organizations.id
requirements
---
id integer PK
app varchar
requirement varchar
optional boolean
release_id integer FK >- releases.id
dependency_id integer FK >- packages.id
short_urls
---
id integer PK
url varchar
short_code varchar
inserted_at timestamp
================================================
FILE: examples/quick_dbd/hexpm/Only-selected-cluster-Accounts-context.qdbd
================================================
audit_logs
---
id integer PK
user_agent varchar
remote_ip varchar
action varchar
params jsonb
user_id integer FK >- users.id
organization_id integer FK >- organizations.id
key_id integer FK >- keys.id
inserted_at timestamp
emails
---
id integer PK
email varchar
verified boolean
primary boolean
public boolean
gravatar boolean
verification_key varchar
verification_expiry timestamp
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
keys
---
id integer PK
name varchar
secret_first varchar
secret_second varchar
public boolean
revoke_at timestamp
inserted_at timestamp
updated_at timestamp
last_use jsonb
user_id integer FK >- users.id
organization_id integer FK >- organizations.id
permissions jsonb
organizations
---
id integer PK
name varchar
billing_active boolean
billing_override boolean
trial_end timestamp
inserted_at timestamp
updated_at timestamp
organization_users
---
id integer PK
role varchar
organization_id integer FK >- organizations.id
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
password_resets
---
id integer PK
key varchar
primary_email varchar
user_id integer FK >- users.id
inserted_at timestamp
sessions
---
id integer PK
token bytea
data jsonb
inserted_at timestamp
updated_at timestamp
users
---
id integer PK
username varchar
full_name varchar
password varchar
service boolean
deactivated_at timestamp
role varchar
inserted_at timestamp
updated_at timestamp
handles jsonb
tfa jsonb
organization_id integer FK - organizations.id
================================================
FILE: examples/quick_dbd/plausible-analytics/Default.qdbd
================================================
schema_migrations
---
version integer PK
inserted_at timestamp
fun_with_flags_toggles
---
id integer PK
flag_name varchar
gate_type varchar
target varchar
enabled boolean
oban_jobs
---
id integer PK
state varchar
queue varchar
worker varchar
args jsonb
meta jsonb
tags array
errors array
attempt integer
attempted_by array
max_attempts integer
priority integer
attempted_at timestamp
cancelled_at timestamp
completed_at timestamp
discarded_at timestamp
inserted_at timestamp
scheduled_at timestamp
api_keys
---
id integer PK
name varchar
scopes array
hourly_request_limit integer
key_hash varchar
key_prefix varchar
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
email_activation_codes
---
id integer PK
code varchar
issued_at timestamp
user_id integer FK >- users.id
invitations
---
id integer PK
invitation_id varchar
email varchar
role "enum(owner,admin,viewer)"
inviter_id integer FK >- users.id
site_id integer FK >- sites.id
inserted_at timestamp
updated_at timestamp
totp_recovery_codes
---
id integer PK
code_digest varchar
user_id integer FK >- users.id
inserted_at timestamp
users
---
id integer PK
email varchar
password_hash varchar
name varchar
last_seen timestamp
trial_expiry_date date
theme "enum(system,light,dark)"
email_verified boolean
previous_email varchar
accept_traffic_until date
allow_next_upgrade_override boolean
totp_enabled boolean
totp_secret bytea
totp_token varchar
totp_last_used_at timestamp
grace_period jsonb
inserted_at timestamp
updated_at timestamp
enterprise_plans
---
id integer PK
paddle_plan_id varchar
billing_interval "enum(yearly,monthly)"
monthly_pageview_limit integer
site_limit integer
team_member_limit integer
features array
hourly_api_request_limit integer
user_id integer FK - users.id
inserted_at timestamp
updated_at timestamp
subscriptions
---
id integer PK
paddle_subscription_id varchar
paddle_plan_id varchar
update_url varchar
cancel_url varchar
status "enum(active,deleted,past_due,paused)"
next_bill_amount varchar
next_bill_date date
last_bill_date date
currency_code varchar
user_id integer FK - users.id
inserted_at timestamp
updated_at timestamp
events_v2
---
name unknown
site_id unknown
hostname varchar
pathname varchar
user_id unknown
session_id unknown
timestamp timestamp
"meta.key" array
"meta.value" array
revenue_source_amount unknown
revenue_source_currency unknown
revenue_reporting_amount unknown
revenue_reporting_currency unknown
referrer varchar
referrer_source varchar
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
sessions_v2
---
hostname varchar
site_id unknown
user_id unknown
session_id unknown
start timestamp
duration unknown
is_bounce unknown
entry_page varchar
exit_page varchar
exit_page_hostname varchar
pageviews unknown
events unknown
sign unknown
"entry_meta.key" array
"entry_meta.value" array
utm_medium varchar
utm_source varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
referrer varchar
referrer_source varchar
country_code unknown
subdivision1_code unknown
subdivision2_code unknown
city_geoname_id unknown
screen_size unknown
operating_system unknown
operating_system_version unknown
browser unknown
browser_version unknown
timestamp timestamp
transferred_from varchar
domains_lookup
---
site_id unknown
domain varchar
funnels
---
id integer PK
name varchar
site_id integer FK >- sites.id
inserted_at timestamp
updated_at timestamp
funnel_steps
---
id integer PK
step_order integer
funnel_id integer FK >- funnels.id
goal_id integer FK >- goals.id
inserted_at timestamp
updated_at timestamp
goals
---
id integer PK
event_name varchar
page_path varchar
currency "enum(KMF,AUD,SAR,BWP,BBD,EGP,YER,CDF,IQD,MRU,JOD,XPT,XBB,NGN,BDT,CNY,ANG,GTQ,HTG,TWD,OMR,STN,AOA,MUR,XCD,TND,THB,KES,GIP,MZN,ERN,MAD,FKP,MVR,BND,KZT,EUR,SYP,MYR,RSD,KRW,COU,GMD,ILS,BAM,XAG,AZN,AFN,AWG,SOS,PAB,AED,UYI,BTN,USN,KPW,IDR,XPD,MOP,GEL,MXV,CHW,XAF,UGX,DJF,SGD,PGK,IRR,VES,PHP,SSP,BOB,XDR,JPY,BHD,UAH,ZAR,BSD,TMT,XOF,XTS,MNT,XSU,XPF,TTD,PLN,AMD,SBD,LSL,GBP,DOP,SEK,MDL,CUP,CZK,SZL,COP,XAU,NOK,CLF,RWF,NAD,KHR,TRY,LAK,SDG,XXX,PEN,LBP,BZD,CLP,KGS,TZS,GNF,KWD,NZD,SVC,LRD,CHF,PKR,SHP,XUA,XBD,LYD,BIF,JMD,ALL,BYN,CUC,UZS,MKD,ZWL,RON,NIO,MMK,SRD,ETB,ARS,GHS,XBA,XBC,UYW,HKD,ISK,DZD,MWK,RUB,SLL,SCR,CHE,CVE,VND,ZMW,HNL,HUF,INR,DKK,FJD,HRK,UYU,PYG,BMD,KYD,VUV,BGN,TOP,MXN,CAD,MGA,BOV,BRL,WST,NPR,CRC,GYD,TJS,LKR,QAR,USD)"
site_id integer FK >- sites.id
inserted_at timestamp
updated_at timestamp
imported_browsers
---
site_id unknown
import_id unknown
date date
browser varchar
browser_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_devices
---
site_id unknown
import_id unknown
date date
device varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_entry_pages
---
site_id unknown
import_id unknown
date date
entry_page varchar
visitors unknown
entrances unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_exit_pages
---
site_id unknown
import_id unknown
date date
exit_page varchar
exits unknown
visitors unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_locations
---
site_id unknown
import_id unknown
date date
country varchar
region varchar
city unknown
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_operating_systems
---
site_id unknown
import_id unknown
date date
operating_system varchar
operating_system_version varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_pages
---
site_id unknown
import_id unknown
date date
hostname varchar
page varchar
visits unknown
visitors unknown
active_visitors unknown
pageviews unknown
exits unknown
time_on_page unknown
site_imports
---
id integer PK
start_date date
end_date date
label varchar
source "enum(noop,csv,universal_analytics,google_analytics_4)"
status "enum(pending,failed,completed,importing)"
legacy boolean
site_id integer FK >- sites.id
imported_by_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
imported_sources
---
site_id unknown
import_id unknown
date date
source varchar
referrer varchar
utm_source varchar
utm_medium varchar
utm_campaign varchar
utm_content varchar
utm_term varchar
visitors unknown
visits unknown
visit_duration unknown
pageviews unknown
bounces unknown
imported_visitors
---
site_id unknown
import_id unknown
date date
visitors unknown
pageviews unknown
bounces unknown
visits unknown
visit_duration unknown
ingest_counters
---
event_timebucket timestamp
site_id unknown
domain unknown
metric unknown
value unknown
plugins_api_tokens
---
id uuid PK
inserted_at timestamp
updated_at timestamp
token_hash bytea
description varchar
hint varchar
last_used_at timestamp
site_id integer FK >- sites.id
shield_rules_country
---
id uuid PK
site_id integer FK >- sites.id
country_code varchar
action "enum(allow,deny)"
added_by varchar
inserted_at timestamp
updated_at timestamp
shield_rules_hostname
---
id uuid PK
site_id integer FK >- sites.id
hostname varchar
hostname_pattern varchar
action "enum(allow,deny)"
added_by varchar
inserted_at timestamp
updated_at timestamp
shield_rules_ip
---
id uuid PK
site_id integer FK >- sites.id
inet inet
action "enum(allow,deny)"
description varchar
added_by varchar
inserted_at timestamp
updated_at timestamp
shield_rules_page
---
id uuid PK
site_id integer FK >- sites.id
page_path varchar
page_path_pattern varchar
action "enum(allow,deny)"
added_by varchar
inserted_at timestamp
updated_at timestamp
sites
---
id integer PK
domain varchar
timezone varchar
public boolean
locked boolean
stats_start_date date
native_stats_start_at timestamp
allowed_event_props array
conversions_enabled boolean
props_enabled boolean
funnels_enabled boolean
ingest_rate_limit_scale_seconds integer
ingest_rate_limit_threshold integer
domain_changed_from varchar
domain_changed_at timestamp
imported_data jsonb
inserted_at timestamp
updated_at timestamp
google_auth
---
id integer PK
email varchar
property varchar
refresh_token varchar
access_token varchar
expires timestamp
user_id integer FK - users.id
site_id integer FK - sites.id
inserted_at timestamp
updated_at timestamp
site_memberships
---
id integer PK
role "enum(owner,admin,viewer)"
site_id integer FK - sites.id
user_id integer FK >- users.id
inserted_at timestamp
updated_at timestamp
monthly_reports
---
id integer PK
recipients array
site_id integer FK - sites.id
inserted_at timestamp
updated_at timestamp
shared_links
---
id integer PK
site_id integer FK >- sites.id
name varchar
slug varchar
password_hash varchar
inserted_at timestamp
updated_at timestamp
spike_notifications
---
id integer PK
recipients array
threshold integer
last_sent timestamp
site_id integer FK - sites.id
inserted_at timestamp
updated_at timestamp
site_user_preferences
---
id integer PK
pinned_at timestamp
user_id integer FK >- users.id
site_id integer FK >- sites.id
inserted_at timestamp
updated_at timestamp
weekly_reports
---
id integer PK
recipients array
site_id integer FK - sites.id
inserted_at timestamp
updated_at timestamp
================================================
FILE: examples_generator.exs
================================================
defmodule Ecto.ERD.ExamplesGenerator do
require Logger
@shared_examples [
[name: "Default", formats: [:dbml, :dot, :qdbd, :puml, :mmd]],
[
name: "No fields",
formats: [:dot, :mmd],
config: """
[
columns: []
]
"""
],
[
name: "Contexts as clusters",
formats: [:dbml, :dot, :puml],
config: """
alias Ecto.ERD.Node
[
map_node: fn
%Node{schema_module: schema_module} = node ->
case Module.split(schema_module) do
[_] -> node
[namespace, _] -> node |> Node.set_cluster(namespace)
parts -> node |> Node.set_cluster(parts |> Enum.take(2) |> Enum.join("."))
end
end
]
"""
],
[
name: "Contexts as clusters (no fields)",
formats: [:dot, :puml],
config: """
alias Ecto.ERD.Node
[
columns: [],
map_node: fn
%Node{schema_module: schema_module} = node ->
case Module.split(schema_module) do
[_] -> node
[namespace, _] -> node |> Node.set_cluster(namespace)
parts -> node |> Node.set_cluster(parts |> Enum.take(2) |> Enum.join("."))
end
end
]
"""
]
]
@data %{
"plausible-analytics" => %{
repo: "git@github.com:plausible/analytics.git",
commit: "ca25b6c7649c6ab9c9268eb57c7931dca1393b94",
examples: @shared_examples
},
"changelog.com" => %{
repo: "git@github.com:thechangelog/changelog.com.git",
commit: "840f6e4fa69e51b680d5462f99db9f5953bf0fdf",
examples:
Enum.filter(@shared_examples, fn example -> example[:name] in ["Default", "No fields"] end) ++
[
[
name: "Clusters",
formats: [:dbml, :dot, :puml],
config: """
alias Ecto.ERD.Node
[
map_node: fn
%Node{schema_module: schema_module} = node ->
cluster_name =
cond do
schema_module in [
Changelog.Episode,
Changelog.EpisodeChapter,
Changelog.EpisodeGuest,
Changelog.EpisodeHost,
Changelog.EpisodeRequest,
Changelog.EpisodeSponsor,
Changelog.EpisodeStat,
Changelog.EpisodeTopic
] ->
"EPISODE"
schema_module in [Changelog.Post, Changelog.PostTopic] ->
"POST"
schema_module in [Changelog.Podcast, Changelog.PodcastHost, Changelog.PodcastTopic] ->
"PODCAST"
schema_module in [Changelog.Sponsor, Changelog.SponsorRep] ->
"SPONSOR"
schema_module in [Changelog.Person, Changelog.Person.Settings] ->
"PERSON"
schema_module in [
Changelog.EpisodeNewsItem,
Changelog.NewsAd,
Changelog.NewsIssue,
Changelog.NewsIssueAd,
Changelog.NewsIssueItem,
Changelog.NewsItem,
Changelog.NewsItemComment,
Changelog.NewsItemTopic,
Changelog.NewsQueue,
Changelog.NewsSource,
Changelog.NewsSponsorship,
Changelog.PostNewsItem
] -> "NEWS"
true ->
nil
end
Node.set_cluster(node, cluster_name)
end
]
"""
]
]
},
"hexpm" => %{
repo: "git@github.com:hexpm/hexpm.git",
commit: "1fb4816abaa8ef23f40795dfa93b9e6b7c569452",
examples:
@shared_examples ++
[
[
name: "Only selected cluster (Accounts context)",
formats: [:dbml, :dot, :qdbd, :puml],
config: """
alias Ecto.ERD.Node
[
map_node: fn
%Node{schema_module: schema_module} = node ->
cluster_name = schema_module |> Module.split() |> Enum.take(2) |> Enum.join(".")
case cluster_name do
"Hexpm.Accounts" -> node |> Node.set_cluster(cluster_name)
_ -> nil
end
end
]
"""
],
[
name: "Only embedded schemas",
formats: [:dot, :puml],
config: """
alias Ecto.ERD.Node
[
map_node: fn
%Node{source: nil} = node -> node
_ -> nil
end
]
"""
]
]
}
}
@formats %{
dot: %{examples_dir: "examples/dot", name: "DOT", image?: true},
dbml: %{examples_dir: "examples/dbml", name: "DBML", image?: false},
qdbd: %{examples_dir: "examples/quick_dbd", name: "QuickDBD", image?: false},
puml: %{examples_dir: "examples/plantuml", name: "PlantUML", image?: true},
mmd: %{examples_dir: "examples/mermaid", name: "Mermaid", image?: false}
}
def run(source_url_root) do
File.mkdir_p("tmp/docs")
Enum.each(@formats, fn {_, %{examples_dir: dir}} -> File.mkdir_p(dir) end)
File.mkdir("tmp/repos")
File.mkdir("tmp/config_files")
Logger.debug("Init projects", ansi_color: :yellow)
Enum.each(@data, fn {project_name, %{repo: repo, commit: commit}} ->
Enum.each(@formats, fn {_, %{examples_dir: dir}} ->
File.mkdir(Path.join(dir, project_name))
end)
File.mkdir(Path.join("tmp/config_files", project_name))
init_project(project_name, repo, commit)
end)
Logger.debug("Generating examples", ansi_color: :yellow)
@data
|> Enum.flat_map(fn {project_name, %{examples: examples}} ->
Enum.map(examples, fn example ->
Task.async(fn -> generate_example(example, project_name) end)
end)
end)
|> Task.yield_many(:infinity)
Logger.debug("Generating markdown docs", ansi_color: :yellow)
Enum.each(@data, &generate_doc(&1, source_url_root))
Logger.debug("Done", ansi_color: :green)
end
defp generate_doc({project_name, %{repo: repo, examples: examples}}, source_url_root) do
examples_md =
examples
|> Enum.map_join("\n\n", fn example ->
config_content =
if example[:config] do
"""
#### Config file
```elixir
# .ecto_erd.exs
#{example[:config]}
```
"""
end
output =
example[:formats]
|> Enum.map(fn
format ->
document_url =
Path.join([
source_url_root,
@formats[format].examples_dir,
project_name,
slugify(example[:name]) <> ".#{format}"
])
image_url =
if @formats[format].image? do
Path.rootname(document_url) <> ".png"
end
[
"**#{@formats[format].name}**"
| Enum.map([document_url, image_url], fn
nil -> "—"
url -> "[View](#{url})"
end)
]
end)
output_content = """
#### Output
#{as_table(output, ["Format", "Document", "Image"])}
"""
"""
## #{example[:name]}
#{output_content}
#{config_content}
"""
end)
content = """
# #{project_name}
Repo: `#{repo}`
#{examples_md}
"""
File.write!(Path.join("tmp/docs", project_name <> ".md"), content)
end
defp generate_example(example, project_name) do
Logger.debug("#{project_name}: generating #{example[:name]}")
slug = slugify(example[:name])
example[:formats]
|> Enum.map(fn
format ->
{format,
Path.expand(
Path.join([@formats[format].examples_dir, project_name, slug <> ".#{format}"])
)}
end)
|> Enum.each(fn {format, output_path} ->
old_output_content =
case File.read(output_path) do
{:ok, content} -> content
{:error, _} -> nil
end
if example[:config] do
config_path = Path.expand(Path.join(["tmp/config_files", project_name, slug <> ".exs"]))
File.write!(config_path, example[:config])
System.cmd(
"mix",
[
"ecto.gen.erd",
"--output-path=#{output_path}",
"--config-path=#{config_path}"
],
cd: Path.join("tmp/repos", project_name)
)
else
System.cmd("mix", ["ecto.gen.erd", "--output-path=#{output_path}"],
cd: Path.join("tmp/repos", project_name)
)
end
{:ok, new_output_content} = File.read(output_path)
if @formats[format].image? and
(old_output_content != new_output_content or
not File.exists?(Path.rootname(output_path) <> ".png")) do
Logger.debug("Generating image from #{output_path}")
generate_image(format, output_path)
end
end)
Logger.debug("#{project_name}: generated #{example[:name]}")
end
defp generate_image(:dot, file) do
System.cmd("dot", ["-Tpng", file, "-o", Path.rootname(file) <> ".png"])
end
defp generate_image(:puml, file) do
System.cmd("plantuml", [file], env: %{"PLANTUML_LIMIT_SIZE" => "8192"})
end
defp init_project(project_name, repo, commit) do
Logger.debug("#{project_name}: clone repo")
System.cmd("git", ["clone", repo, project_name], cd: "tmp/repos")
System.cmd("git", ["fetch", "origin"], cd: Path.join("tmp/repos", project_name))
System.cmd("git", ["checkout", "--", "."], cd: Path.join("tmp/repos", project_name))
System.cmd("git", ["checkout", commit], cd: Path.join("tmp/repos", project_name))
add_ecto_erd_to_dependencies(Path.join(["tmp/repos", project_name, "mix.exs"]))
Logger.debug("#{project_name}: get dependencies")
System.cmd("mix", ["deps.get"], cd: Path.join("tmp/repos", project_name))
Logger.debug("#{project_name}: compile")
{_, 0} = System.cmd("mix", ["compile"], cd: Path.join("tmp/repos", project_name))
end
defp slugify(name) do
name |> String.replace(~r/\s+/, "-") |> String.replace(~r/[^\w-]+/, "")
end
defp add_ecto_erd_to_dependencies(path) do
dependency_line = inspect({:ecto_erd, path: "../../../"})
content = File.read!(path)
if String.contains?(content, dependency_line) do
Logger.debug("#{dependency_line} already present in #{path}")
else
new_content =
content
|> String.replace(~r|defp? deps(?:\(\))? do\n( +)\[|, ~s|\\0\n\\1 #{dependency_line},|)
File.write!(path, new_content)
Logger.debug("Added #{dependency_line} to #{path}")
end
end
def projects, do: Map.keys(@data)
defp as_table(rows, header) do
columns_number = length(header)
column_widths =
1..columns_number
|> Map.new(fn column_number ->
max_length =
[header | rows]
|> Enum.map(fn row ->
row |> Enum.at(column_number - 1) |> to_string |> String.length()
end)
|> Enum.max()
{column_number, max_length}
end)
header_delimiter =
1..columns_number
|> Enum.map(fn column_number -> String.duplicate("-", column_widths[column_number]) end)
([header, header_delimiter] ++ rows)
|> Enum.map(fn row ->
body =
row
|> Enum.with_index(1)
|> Enum.map_join(" | ", fn {cell, column_number} ->
cell |> to_string() |> String.pad_trailing(column_widths[column_number])
end)
"| " <> body <> " |"
end)
|> Enum.intersperse("\n")
end
end
================================================
FILE: lib/ecto/erd/color.ex
================================================
defmodule Ecto.ERD.Color do
@moduledoc false
@colors ~w(
#eedfcc
#f0ffff
#eee5de
#fffafa
#f0f8ff
#8deeee
#b4eeb4
#eee685
#eee5de
#ffefd5
)
def get(term) do
Enum.at(@colors, :erlang.phash2(term, length(@colors)))
end
end
================================================
FILE: lib/ecto/erd/document/dbml.ex
================================================
defmodule Ecto.ERD.Document.DBML do
@moduledoc false
alias Ecto.ERD.{Node, Field, Edge, Graph, Render}
@behaviour Ecto.ERD.Document
@impl true
def schemaless?, do: true
@impl true
def render(%Graph{nodes: nodes, edges: edges}, _options) do
groups =
nodes
|> Enum.group_by(& &1.cluster, & &1.source)
|> Map.delete(nil)
|> Enum.map_join(fn {cluster_name, sources} ->
"""
TableGroup #{Render.in_quotes(cluster_name)} {
#{Enum.map_join(sources, "\n ", &Render.in_quotes/1)}
}
"""
end)
enums_mapping = enums_mapping(nodes)
enums =
enums_mapping
|> Map.values()
|> Enum.uniq()
|> Enum.map_join("\n", fn {name, values} ->
"""
Enum #{Render.in_quotes(name)} {
#{values |> Enum.map_join("\n ", &Render.in_quotes/1)}
}
"""
end)
tables =
Enum.map_join(nodes, "\n", fn %Node{source: source, fields: fields} ->
enum_name_by_field_name = fn field_name ->
{enum_name, _values} = enums_mapping[[source, field_name]]
enum_name
end
"""
Table #{Render.in_quotes(source)} {
#{Enum.map_join(fields, "\n ", &render_field(&1, enum_name_by_field_name))}
}
"""
end)
refs =
edges
|> Enum.map(&render_edge/1)
|> Enum.reject(&is_nil/1)
|> Enum.join("\n")
groups <> "\n" <> enums <> "\n" <> tables <> "\n" <> refs
end
defp render_edge(%Edge{to: {nil, _, _}}), do: nil
defp render_edge(%Edge{
from: {from_source, _from_schema, {:field, from_field}},
to: {to_source, _to_schema, {:field, to_field}},
assoc_types: assoc_types
}) do
operator =
if {:has, :one} in assoc_types do
"-"
else
"<"
end
[
"Ref:",
Render.in_quotes(from_source) <> "." <> Render.in_quotes(from_field),
operator,
Render.in_quotes(to_source) <> "." <> Render.in_quotes(to_field)
]
|> Enum.join(" ")
end
# tries to cut name from #source_#field format to just #field
@doc false
def enums_mapping(nodes) do
nodes
|> Enum.flat_map(fn %Node{source: source, fields: fields} ->
fields
|> Enum.flat_map(fn
%Field{name: name, type: {:parameterized, {Ecto.Enum, %{on_dump: on_dump}}}} ->
values = on_dump |> Map.values() |> Enum.sort()
[{source, name, values}]
_ ->
[]
end)
end)
|> Enum.group_by(fn {_source, name, _values} -> name end, fn {source, _name, values} ->
{source, values}
end)
|> Enum.flat_map(fn {name, items} ->
values_to_sources =
Enum.group_by(items, fn {_source, values} -> values end, fn {source, _values} ->
source
end)
enum_name =
if map_size(values_to_sources) == 1 do
fn _ -> to_string(name) end
else
fn source -> "#{source}_#{name}" end
end
Enum.map(items, fn {source, values} ->
{[source, name], {enum_name.(source), values}}
end)
end)
|> Map.new()
end
defp render_field(%Field{name: name, type: type, primary?: primary?}, enum_name_by_field_name) do
settings =
if primary? do
" [pk]"
else
""
end
case type do
{:parameterized, {Ecto.Enum, _}} ->
"#{Render.in_quotes(name)} #{Render.in_quotes(enum_name_by_field_name.(name))}#{settings}"
_ ->
"#{Render.in_quotes(name)} #{format_type(type)}#{settings}"
end
end
defp format_type(type) do
case Ecto.Type.type(type) do
{:array, _t} -> "array"
:id -> "integer"
:identity -> "bigint"
:binary_id -> "uuid"
:string -> "varchar"
:binary -> "bytea"
:map -> "jsonb"
{:map, _} -> "jsonb"
:time_usec -> "time"
:utc_datetime -> "timestamp"
:utc_datetime_usec -> "timestamp"
:naive_datetime -> "timestamp"
:naive_datetime_usec -> "timestamp"
atom when is_atom(atom) -> Atom.to_string(atom)
{:parameterized, _} -> "unknown"
end
end
end
================================================
FILE: lib/ecto/erd/document/dot.ex
================================================
defmodule Ecto.ERD.Document.Dot do
@moduledoc false
alias Ecto.ERD.{HTML, Edge, Node, Field, Graph, Render}
@behaviour Ecto.ERD.Document
@impl true
def schemaless?, do: false
@impl true
def render(%Graph{nodes: nodes, edges: edges}, opts) do
fontname = opts[:fontname] || "Roboto Mono"
columns = opts[:columns] || [:name, :type]
clusters = Enum.group_by(nodes, & &1.cluster)
{global_nodes, clusters} = Map.pop(clusters, nil)
global_nodes = List.wrap(global_nodes)
subgraphs =
Enum.map(clusters, fn {cluster_name, nodes} ->
"""
subgraph #{Render.in_quotes("cluster_#{cluster_name}")} {
style=filled
fontname=#{Render.in_quotes(fontname)}
color = #{Render.in_quotes(Ecto.ERD.Color.get(cluster_name))}
label = <#{{:font, ["point-size": 24], {:b, [], cluster_name}} |> HTML.to_iodata()}>
#{Enum.map_join(nodes, "\n ", &render_node(&1, columns))}
}
"""
end)
strict? = columns == []
"""
#{if strict?, do: "strict "}digraph {
ranksep=1.0; rankdir=LR;
node [shape = none, fontname=#{Render.in_quotes(fontname)}];
#{Enum.map_join(global_nodes, "\n ", &render_node(&1, columns))}
#{subgraphs}
#{Enum.map_join(edges, "\n ", &render_edge(&1, columns == []))}
}
"""
end
defp render_edge(
%Edge{
from: from,
to: to,
assoc_types: assoc_types
},
skip_port?
) do
result = "#{render_position(from, skip_port?)}:e -> #{render_position(to, skip_port?)}:w"
# don't draw arrow if relation is 1 <-> 1
if {:has, :one} in assoc_types do
result <> " [dir=none]"
else
result
end
end
defp render_position({source, schema_module, port}, skip_port?) do
string = Render.in_quotes(Node.id(source, schema_module))
if skip_port?, do: string, else: string <> ":" <> Render.in_quotes(Edge.port_name(port))
end
defp render_node(
%Node{
fields: fields,
source: source,
schema_module: schema_module
},
columns
) do
field_rows =
if columns == [] or fields == [] do
[]
else
column_width =
Map.new(
columns,
fn column ->
max_length =
fields
|> Enum.map(fn field -> field |> format_field(column) |> String.length() end)
|> Enum.max()
{column, max_length + 5}
end
)
Enum.map(fields, fn %Field{name: name} = field ->
{:tr, [],
{:td, [align: :left, port: Edge.port_name({:field, name})],
Enum.map(columns, fn
column ->
text = String.pad_trailing(format_field(field, column), column_width[column])
case column do
:type -> {:i, [], {:font, [color: :gray54], text}}
:name -> if field.primary?, do: {:b, [], text}, else: text
end
end)}}
end)
end
table =
{:table,
[align: :left, border: 1, style: :rounded, cellspacing: 0, cellpadding: 4, cellborder: 0],
[
if(schema_module,
do:
{:tr, [],
{:td,
if(not is_nil(source) or Enum.empty?(field_rows),
do: [],
else: [border: 1, sides: :b, colspan: length(columns)]
) ++
[
port: Edge.port_name({:header, :schema_module})
], {:font, ["point-size": 18], " " <> inspect(schema_module) <> " "}}}
),
if(source,
do:
{:tr, [],
{:td,
if(Enum.empty?(field_rows),
do: [],
else: [border: 1, sides: :b, colspan: length(columns)]
),
[
{:font, ["point-size": 14], {:i, [], source}}
]}}
),
field_rows
]}
|> HTML.to_iodata()
Render.in_quotes(Node.id(source, schema_module)) <> " [label= <#{table}>]"
end
defp format_field(%Field{name: name}, :name), do: inspect(name)
defp format_field(%Field{type: type}, :type), do: format_type(type)
defp format_type({:parameterized, {Ecto.Enum, %{on_dump: on_dump}}}) do
"#Enum<#{inspect(Enum.sort(Map.keys(on_dump)), limit: 10)}>"
end
defp format_type(
{:parameterized,
{Ecto.Embedded, %Ecto.Embedded{cardinality: cardinality, related: related}}}
) do
"#Ecto.Embedded<#{inspect([{cardinality, related}])}>"
end
defp format_type({:array, type}) do
"{:array, #{format_type(type)}}"
end
defp format_type(type), do: inspect(type)
end
================================================
FILE: lib/ecto/erd/document/mermaid.ex
================================================
defmodule Ecto.ERD.Document.Mermaid do
@moduledoc false
alias Ecto.ERD.{Node, Field, Edge, Graph}
@behaviour Ecto.ERD.Document
require Logger
@impl true
def schemaless?, do: true
@impl true
def render(%Graph{nodes: nodes, edges: edges}, opts) do
fields_config =
case opts[:columns] || [:type, :name] do
[] ->
:hide_fields
[:type, :name] ->
:show_fields
_ ->
raise """
Mermaid doesn't support rich customization of columns.
You should either set :columns to `[]` in order to hide fields or keep the default value.
"""
end
"""
erDiagram
#{nodes |> Enum.map(&render_node(&1, fields_config)) |> Enum.reject(&is_nil/1) |> Enum.join("\n ")}
#{edges |> Enum.map(&render_edge/1) |> Enum.reject(&is_nil/1) |> Enum.join("\n ")}
"""
end
defp render_node(%Node{source: source, fields: fields}, fields_config) do
if name_valid?(source) do
case fields_config do
:show_fields ->
[
source,
" {\n",
fields
|> Enum.map(&render_field/1)
|> Enum.reject(&is_nil/1)
|> Enum.map(&[" ", &1])
|> Enum.intersperse("\n"),
"\n }"
]
:hide_fields ->
source
end
else
Logger.warning("Source #{inspect(source)} contains invalid symbols and cannot be displayed")
nil
end
end
defp render_edge(%Edge{
from: {from_source, _from_schema, {:field, _from_field}},
to: {to_source, _to_schema, {:field, _to_field}},
assoc_types: assoc_types
}) do
if name_valid?(from_source) and name_valid?(to_source) do
operator =
if {:has, :one} in assoc_types do
"||--o|"
else
"||--|{"
end
[
from_source,
operator,
to_source,
":",
"\"\""
]
|> Enum.join(" ")
end
end
defp render_field(%Field{} = field) do
if name_valid?(to_string(field.name)) do
format_type(field.type) <>
" " <>
to_string(field.name) <>
if field.primary? do
" PK"
else
""
end <>
if field.comment do
~s( "#{field.comment}")
else
""
end
else
Logger.warning(
"Field name #{inspect(field.name)} contains invalid symbols and cannot be displayed"
)
nil
end
end
defp format_type(type) do
case Ecto.Type.type(type) do
{:array, _t} -> "array"
:id -> "integer"
:identity -> "bigint"
:binary_id -> "uuid"
:string -> "varchar"
:binary -> "bytea"
:map -> "jsonb"
{:map, _} -> "jsonb"
:time_usec -> "time"
:utc_datetime -> "timestamp"
:utc_datetime_usec -> "timestamp"
:naive_datetime -> "timestamp"
:naive_datetime_usec -> "timestamp"
atom when is_atom(atom) -> Atom.to_string(atom)
{:parameterized, _} -> "unknown"
end
end
defp name_valid?(source_or_field) do
source_or_field =~ ~r/^[a-zA-z\-_\d]+$/
end
end
================================================
FILE: lib/ecto/erd/document/plantuml.ex
================================================
defmodule Ecto.ERD.Document.PlantUML do
@moduledoc false
alias Ecto.ERD.{Node, Edge, Graph, Render, Color}
@behaviour Ecto.ERD.Document
@safe_name_pattern ~r/^[a-z\d_\.:\?]+$/i
@impl true
def schemaless?, do: false
@impl true
def render(%Graph{nodes: nodes, edges: edges}, opts) do
fontname = opts[:fontname] || "Roboto Mono"
columns = opts[:columns] || [:name, :type]
clusters = Enum.group_by(nodes, & &1.cluster)
{global_nodes, clusters} = Map.pop(clusters, nil)
ensure_cluster_names_valid!(Map.keys(clusters))
global_nodes = List.wrap(global_nodes)
namespaces =
Enum.map(clusters, fn {cluster_name, nodes} ->
"""
namespace #{cluster_name} #{Color.get(cluster_name)} {
#{Enum.map_join(nodes, "\n", &render_node(&1, columns, " "))}
}
"""
end)
entities = Enum.map_join(global_nodes, "\n", &render_node(&1, columns, ""))
refs =
edges
|> Enum.uniq_by(fn %Edge{
from: {from_source, from_schema, _},
to: {to_source, to_schema, _}
} ->
{from_source, from_schema, to_source, to_schema}
end)
|> Enum.map_join("\n", &render_edge/1)
"""
@startuml
set namespaceSeparator none
hide circle
hide methods
#{case columns do
[] -> "hide fields"
_ -> ""
end}
skinparam linetype ortho
skinparam defaultFontName #{fontname}
skinparam shadowing false
#{namespaces}
#{entities}
#{refs}
@enduml
"""
end
defp render_node(
%Node{
source: source,
fields: fields,
schema_module: schema_module
},
columns,
padding
) do
case columns do
[] ->
"#{padding}entity #{Render.in_quotes(Node.id(source, schema_module), @safe_name_pattern)}"
columns ->
items =
case Enum.split_with(fields, & &1.primary?) do
{[], fields} -> fields
{pk, fields} -> pk ++ ["--"] ++ fields
end
content =
Enum.map_join(items, "\n#{padding} ", fn
"--" ->
"--"
field ->
columns
|> Enum.map_join(
" : ",
fn
:name -> Render.in_quotes(field.name, @safe_name_pattern)
:type -> format_type(field.type)
end
)
end)
"""
#{padding}entity #{Render.in_quotes(Node.id(source, schema_module), @safe_name_pattern)} {
#{padding} #{content}
#{padding}}
"""
end
end
defp render_edge(%Edge{
from: {from_source, from_schema, _},
to: {to_source, to_schema, _},
assoc_types: assoc_types
}) do
operator =
if {:has, :one} in assoc_types do
"||--o|"
else
"||--|{"
end
[
Render.in_quotes(Node.id(from_source, from_schema), @safe_name_pattern),
operator,
Render.in_quotes(Node.id(to_source, to_schema), @safe_name_pattern)
]
|> Enum.join(" ")
end
defp format_type({:parameterized, {Ecto.Enum, %{on_dump: on_dump}}}) do
elements_limit = 10
values = Map.values(on_dump)
length = length(values)
values =
if length <= elements_limit do
values
else
values
|> Enum.slice(0, elements_limit)
|> List.replace_at(-1, "...")
end
"enum(#{Enum.join(values, ",")})"
end
defp format_type(type) do
case Ecto.Type.type(type) do
{parent, _t} -> Atom.to_string(parent)
atom when is_atom(atom) -> Atom.to_string(atom)
{:parameterized, _} -> "unknown"
end
end
# namespaces cannot be quoted, so this check is necessary
defp ensure_cluster_names_valid!(names) do
Enum.each(names, fn name ->
unless name =~ @safe_name_pattern do
raise "Cluster name #{inspect(name)} contains symbols which are unsupported by PlantUML."
end
end)
end
end
================================================
FILE: lib/ecto/erd/document/quick_dbd.ex
================================================
defmodule Ecto.ERD.Document.QuickDBD do
@moduledoc false
alias Ecto.ERD.{Node, Field, Edge, Graph, Render}
@behaviour Ecto.ERD.Document
@impl true
def schemaless?, do: true
@impl true
def render(%Graph{nodes: nodes, edges: edges}, _opts) do
foreign_keys_mapping =
Map.new(edges, fn %Edge{to: {to_source, _to_schema, {:field, to_field}}} = edge ->
{{to_source, to_field}, edge}
end)
Enum.map_join(nodes, "\n\n", &render_node(&1, foreign_keys_mapping))
end
defp render_node(%Node{source: source, fields: fields}, foreign_keys_mapping) do
get_fkey_edge = fn field_name -> foreign_keys_mapping[{source, field_name}] end
[
source,
"\n---\n",
fields
|> Enum.map(&render_field(&1, get_fkey_edge))
|> Enum.intersperse("\n")
]
end
defp render_field(%Field{name: name, type: type, primary?: primary?}, get_fkey_edge) do
settings =
if primary? do
["PK"]
else
case get_fkey_edge.(name) do
%Edge{
from: {from_source, _from_schema, {:field, from_field}},
assoc_types: assoc_types
} ->
operator =
if {:has, :one} in assoc_types do
"-"
else
">-"
end
[
"FK #{operator} #{Render.in_quotes(from_source)}.#{Render.in_quotes(from_field)}"
]
_ ->
[]
end
end
Enum.intersperse(
[Render.in_quotes(name), Render.in_quotes(format_type(type)) | settings],
" "
)
end
defp format_type({:parameterized, {Ecto.Enum, %{on_dump: on_dump}}}) do
"enum(#{Enum.join(Map.values(on_dump), ",")})"
end
defp format_type(type) do
case Ecto.Type.type(type) do
{:array, _t} -> "array"
:id -> "integer"
:identity -> "bigint"
:binary_id -> "uuid"
:string -> "varchar"
:binary -> "bytea"
:map -> "jsonb"
{:map, _} -> "jsonb"
:time_usec -> "time"
:utc_datetime -> "timestamp"
:utc_datetime_usec -> "timestamp"
:naive_datetime -> "timestamp"
:naive_datetime_usec -> "timestamp"
atom when is_atom(atom) -> Atom.to_string(atom)
{:parameterized, _} -> "unknown"
end
end
end
================================================
FILE: lib/ecto/erd/document.ex
================================================
defmodule Ecto.ERD.Document do
@moduledoc false
@callback render(Ecto.ERD.Graph.t(), opts :: Keyword.t()) :: iodata()
@callback schemaless?() :: boolean()
def render(schema_modules, format, map_node_callback, render_opts)
when is_function(map_node_callback, 1) do
document_module =
case format do
".dbml" -> Ecto.ERD.Document.DBML
".dot" -> Ecto.ERD.Document.Dot
".mmd" -> Ecto.ERD.Document.Mermaid
".puml" -> Ecto.ERD.Document.PlantUML
".qdbd" -> Ecto.ERD.Document.QuickDBD
format -> raise "Unsupported format #{format}"
end
schema_modules
|> Ecto.ERD.Graph.new(
if document_module.schemaless?() do
[:associations]
else
[:associations, :embeds]
end
)
|> Ecto.ERD.Graph.map_nodes(map_node_callback)
|> then(
if document_module.schemaless?() do
&Ecto.ERD.Graph.make_schemaless/1
else
&Function.identity/1
end
)
|> Ecto.ERD.Graph.sort()
|> document_module.render(render_opts)
end
end
================================================
FILE: lib/ecto/erd/edge.ex
================================================
defmodule Ecto.ERD.Edge do
@moduledoc false
defstruct [:from, :to, :assoc_types]
def new(%{
from: {_source1, _module1, _port1} = from,
to: {_source2, _module2, _port2} = to,
assoc_types: assoc_types
})
when is_list(assoc_types) do
%__MODULE__{
from: from,
to: to,
assoc_types: assoc_types
}
end
def connected_with_node?(
%__MODULE__{
from: {source1, module1, _port1},
to: {source2, module2, _port2}
},
node
) do
(source1 == node.source and module1 == node.schema_module) or
(source2 == node.source and module2 == node.schema_module)
end
def merge(%__MODULE__{} = edge1, %__MODULE__{} = edge2) do
%__MODULE__{
edge1
| assoc_types: edge1.assoc_types ++ edge2.assoc_types
}
end
def port_name({type, id}) when type in [:header, :field] and is_atom(id), do: "#{type}@#{id}"
end
================================================
FILE: lib/ecto/erd/field.ex
================================================
defmodule Ecto.ERD.Field do
@moduledoc """
Field struct.
Represents the data for an individual field in `Ecto.ERD.Node`.
You can update fields on this struct in `.ecto_erd.exs` if needed.
"""
defstruct [:name, :type, :primary?, :comment]
@typedoc """
A field comment.
Rendering comments is currently supported only by the Mermaid format.
## Example
# File: .ecto_erd.exs
[
map_node: fn
%Ecto.ERD.Node{schema_module: schema_module} = node ->
update_in(node, [Access.key(:fields), Access.all()], fn field ->
# `DocumentationLib` is a fictional module that returns documentation about a field
case DocumentationLib.doc({schema_module, field}) do
nil ->
field
doc ->
%{field | comment: doc}
end
end)
end
]
"""
@type comment :: String.t()
@type t :: %__MODULE__{
name: atom(),
type: atom() | tuple(),
primary?: boolean(),
comment: comment() | nil
}
def new(%{name: name, type: type} = params) do
%__MODULE__{
name: name,
type: type,
primary?: Map.get(params, :primary?, false)
}
end
end
================================================
FILE: lib/ecto/erd/graph.ex
================================================
defmodule Ecto.ERD.Graph do
@moduledoc false
alias Ecto.ERD.{Edge, Node, Field}
defstruct [:edges, :nodes]
require Logger
@type t :: %__MODULE__{}
def new(modules, relation_types) do
data =
modules
|> Enum.flat_map(fn module -> components(module, relation_types) end)
|> Enum.group_by(fn
%Edge{} -> :edges
%Node{} -> :nodes
end)
%__MODULE__{
# multiple nodes could be generated by multiple schemas which use the same table in many to many relation
nodes: Enum.uniq(Map.get(data, :nodes, [])),
edges: merge_edges_with_same_direction(Map.get(data, :edges, []))
}
end
def sort(%__MODULE__{nodes: nodes, edges: edges}) do
%__MODULE__{
nodes: Enum.sort_by(nodes, &{&1.cluster, &1.schema_module, &1.source}),
edges: Enum.sort_by(edges, &{&1.from, &1.to})
}
end
def map_nodes(%__MODULE__{nodes: nodes, edges: edges}, map_node_callback)
when is_function(map_node_callback, 1) do
{nodes, removed_nodes} =
Enum.flat_map_reduce(nodes, [], fn node, removed_nodes ->
case map_node_callback.(node) do
nil -> {[], [node | removed_nodes]}
node -> {[node], removed_nodes}
end
end)
edges =
Enum.reject(edges, fn edge ->
Enum.any?(removed_nodes, fn node -> Edge.connected_with_node?(edge, node) end)
end)
%__MODULE__{
nodes: List.wrap(nodes),
edges: edges
}
end
# it, actually, doesn't remove ALL information about schemas, but only in duplicated entities -
# when nodes have the same source or when edges are equal by source and port.
def make_schemaless(%__MODULE__{nodes: nodes, edges: edges}) do
nodes =
nodes
|> Enum.group_by(fn %Node{source: source} -> source end)
|> Map.delete(nil)
|> Enum.map(fn {_source, nodes} -> Enum.reduce(nodes, &Node.merge_to_schemaless/2) end)
edges =
edges
|> Enum.uniq_by(fn %Edge{from: {source1, _module1, port1}, to: {source2, _module2, port2}} ->
{{source1, port1}, {source2, port2}}
end)
%__MODULE__{
nodes: nodes,
edges: edges
}
end
defp merge_edges_with_same_direction(edges) do
edges
|> Enum.group_by(fn %Edge{from: from, to: to} -> {from, to} end)
|> Enum.map(fn {_direction, edges} -> Enum.reduce(edges, &Edge.merge/2) end)
end
defp components(schema_module, relation_types) do
if ecto_schema?(schema_module) do
relation_components =
Enum.flat_map(relation_types, &components_from_relations(schema_module, &1))
primary_keys = schema_module.__schema__(:primary_key)
node =
Node.new(
schema_module,
schema_module.__schema__(:source),
Enum.map(
schema_module.__schema__(:fields),
fn field ->
Field.new(%{
name: field,
type: schema_module.__schema__(:type, field),
primary?: field in primary_keys
})
end
)
)
[node | relation_components]
else
Logger.warning("Skipping schema #{schema_module}: not found")
[]
end
end
defp components_from_relations(module, :associations) do
:associations
|> module.__schema__()
|> Enum.flat_map(fn assoc_field ->
from_relation_struct(module.__schema__(:association, assoc_field))
end)
end
defp components_from_relations(module, :embeds) do
:embeds
|> module.__schema__()
|> Enum.flat_map(fn embed_field ->
from_relation_struct(module.__schema__(:embed, embed_field))
end)
end
defp from_relation_struct(%Ecto.Embedded{
owner: owner,
related: related,
field: field,
cardinality: cardinality
}) do
if ecto_schema?(related) do
[
Edge.new(%{
from: {owner.__schema__(:source), owner, {:field, field}},
to: {related.__schema__(:source), related, {:header, :schema_module}},
assoc_types: [has: cardinality]
})
]
else
Logger.warning(
"Skipping association `embeds_#{cardinality} #{inspect(field)}` in schema #{inspect(owner)}: #{inspect(related)} is not an Ecto schema"
)
[]
end
end
defp from_relation_struct(%Ecto.Association.BelongsTo{
owner: owner,
owner_key: owner_key,
related: related,
related_key: related_key,
field: field
}) do
if ecto_schema?(related) do
related_source = related.__schema__(:source)
owner_source = owner.__schema__(:source)
[
Edge.new(%{
from: {related_source, related, {:field, related_key}},
to: {owner_source, owner, {:field, owner_key}},
assoc_types: [:belongs_to]
})
]
else
Logger.warning(
"Skipping association `belongs_to #{inspect(field)}` in schema #{inspect(owner)}: #{inspect(related)} is not an Ecto schema"
)
[]
end
end
defp from_relation_struct(%Ecto.Association.Has{
owner: owner,
owner_key: owner_key,
related: related,
related_key: related_key,
cardinality: cardinality,
field: field
}) do
if ecto_schema?(related) do
related_source = related.__schema__(:source)
owner_source = owner.__schema__(:source)
[
Edge.new(%{
from: {owner_source, owner, {:field, owner_key}},
to: {related_source, related, {:field, related_key}},
assoc_types: [has: cardinality]
})
]
else
Logger.warning(
"Skipping association `has_#{cardinality} #{inspect(field)}` in schema #{inspect(owner)}: #{inspect(related)} is not an Ecto schema"
)
[]
end
end
defp from_relation_struct(%Ecto.Association.ManyToMany{
join_through: join_through,
owner: owner,
field: field,
related: related,
join_keys: [{join_source_owner_fk, owner_pk}, {join_source_related_fk, related_pk}]
}) do
if ecto_schema?(related) do
if is_atom(join_through) and not ecto_schema?(join_through) do
Logger.warning(
"Skipping association `many_to_many #{inspect(field)}` in schema #{inspect(owner)}: #{inspect(join_through)} is not an Ecto schema"
)
[]
else
{join_module, join_source} =
case join_through do
value when is_atom(value) ->
{value, value.__schema__(:source)}
value when is_binary(value) ->
{nil, value}
end
nodes =
case join_module do
nil ->
fields = [
Field.new(%{
name: join_source_owner_fk,
type: owner.__schema__(:type, owner_pk),
primary?: false
}),
Field.new(%{
name: join_source_related_fk,
type: related.__schema__(:type, related_pk),
primary?: false
})
]
[Node.new(join_source, Enum.sort(fields))]
_join_module ->
[]
end
nodes ++
[
Edge.new(%{
from: {owner.__schema__(:source), owner, {:field, owner_pk}},
to: {join_source, join_module, {:field, join_source_owner_fk}},
assoc_types: [has: :many]
}),
Edge.new(%{
from: {related.__schema__(:source), related, {:field, related_pk}},
to: {join_source, join_module, {:field, join_source_related_fk}},
assoc_types: [has: :many]
})
]
end
else
Logger.warning(
"Skipping association `many_to_many #{inspect(field)}` in schema #{inspect(owner)}: module #{inspect(related)} is not an Ecto schema"
)
[]
end
end
defp from_relation_struct(%Ecto.Association.HasThrough{}) do
[]
end
defp ecto_schema?(module) do
function_exported?(module, :__schema__, 1)
end
end
================================================
FILE: lib/ecto/erd/html.ex
================================================
defmodule Ecto.ERD.HTML do
@moduledoc false
def to_iodata(tuples) when is_list(tuples) do
Enum.map(tuples, &to_iodata/1)
end
def to_iodata({tag_name, attrs, content}) do
tag(
to_string(tag_name),
attrs,
content
|> List.wrap()
|> to_iodata()
)
end
def to_iodata(term), do: HtmlEntities.encode(to_string(term))
defp tag(name, attrs, content) do
["<", name, attrs(attrs), ">", content, "", name, ">"]
end
defp attrs([]), do: []
defp attrs(attrs) do
[
" ",
Enum.map_intersperse(attrs, " ", fn
{key, value} -> [to_string(key), "='", to_string(value), "'"]
end)
]
end
end
================================================
FILE: lib/ecto/erd/node.ex
================================================
defmodule Ecto.ERD.Node do
@moduledoc """
Node struct.
* If `source` is `nil`, then `schema_module` cannot be `nil` and node describes embedded schema.
* If `source` is not `nil` and `schema_module` is not `nil`, then the node describes a regular schema.
* If `schema_module` is `nil`, then `source` cannot be `nil`, and the node describes a source (table) that was
automatically inferred from many-to-many relations.
* If `cluster` is `nil`, then the node is rendered outside any cluster.
"""
defstruct [
:source,
:schema_module,
:fields,
:cluster
]
@type t() :: %__MODULE__{
source: nil | String.t(),
schema_module: nil | module(),
fields: [Ecto.ERD.Field.t()],
cluster: nil | String.t()
}
@doc """
Set a `cluster` for the given `node`.
A cluster is a group of nodes that are displayed together.
"""
@spec set_cluster(t(), nil | String.t()) :: t()
def set_cluster(%__MODULE__{} = node, cluster) when is_nil(cluster) or is_binary(cluster) do
%{node | cluster: cluster}
end
@doc false
def id(source, schema_module)
when (is_nil(source) or is_binary(source)) and is_atom(schema_module) do
if schema_module do
inspect(schema_module)
else
source
end
end
@doc false
def new(schema_module \\ nil, source, fields) do
%__MODULE__{
schema_module: schema_module,
source: source,
fields: fields
}
end
@doc false
def merge_to_schemaless(
%__MODULE__{source: source, fields: fields1, cluster: cluster1},
%__MODULE__{
source: source,
fields: fields2,
cluster: cluster2
}
)
when not is_nil(source) do
# If fields have different types with the same name, only one type will be chosen
fields = Enum.uniq_by(fields1 ++ fields2, & &1.name)
cluster =
case {cluster1, cluster2} do
{nil, cluster} ->
cluster
{cluster, nil} ->
cluster
{cluster, cluster} ->
cluster
{cluster1, cluster2} ->
IO.warn(
"Trying to merge two nodes with source #{inspect(source)} but with different clusters " <>
"(#{inspect(cluster1)} and #{inspect(cluster2)}); removing the cluster in favor of the global space"
)
nil
end
%__MODULE__{
source: source,
fields: fields,
cluster: cluster
}
end
end
================================================
FILE: lib/ecto/erd/render.ex
================================================
defmodule Ecto.ERD.Render do
@moduledoc false
def in_quotes(value, pattern \\ ~r/^[a-z\d_]+$/i) do
value = to_string(value)
# avoid redundant quotes if possible
if value =~ pattern do
value
else
"\"" <> String.replace(value, "\"", "\\\"") <> "\""
end
end
end
================================================
FILE: lib/ecto/erd/schema_modules.ex
================================================
defmodule Ecto.ERD.SchemaModules do
@moduledoc false
def scan(otp_app) do
{:ok, applications} = :application.get_key(otp_app, :applications)
[otp_app | applications]
|> Enum.flat_map(fn application ->
case :application.get_key(application, :modules) do
{:ok, modules} ->
modules
_error ->
[]
end
end)
|> Enum.filter(fn module ->
Code.ensure_loaded!(module)
function_exported?(module, :__schema__, 1) and module != Ecto.Schema
end)
end
end
================================================
FILE: lib/mix/tasks/ecto.gen.erd.ex
================================================
defmodule Mix.Tasks.Ecto.Gen.Erd do
@moduledoc """
A Mix task that generates an ERD (Entity‑Relationship Diagram) in various formats.
Supported formats:
* [DOT](#module-dot) (default)
* [PlantUML](#module-plantuml)
* [DBML](#module-dbml)
* [QuickDBD](#module-quickdbd)
* [Mermaid](#module-mermaid)
Configuration examples and sample output for a few open-source projects can be found in the PAGES section under EXAMPLES.
## DOT
The [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) format can represent all available entity types:
* schemas
* embedded schemas
* schemaless tables (automatically derived from many-to-many relations)
Clusters are supported and can be set via the `:map_node` option using `Ecto.ERD.Node.set_cluster/2`.
Install [Graphviz](https://graphviz.org/) to convert a `*.dot` file to an image.
```
$ mix ecto.gen.erd # generates ecto_erd.dot
$ mix ecto.gen.erd --output-path=ecto_erd.dot
$ mix ecto.gen.erd && dot -Tpng ecto_erd.dot -o erd.png && xdg-open erd.png
```
## PlantUML
[PlantUML](https://plantuml.com) supports the same features as DOT.
Install [PlantUML](https://plantuml.com/download) to convert a `*.puml` file to an image.
```
$ mix ecto.gen.erd --output-path=erd.puml
$ mix ecto.gen.erd --output-path=erd.puml && plantuml erd.puml && xdg-open erd.png
```
*Tip: If the output image is cropped, adjust the image size with the `PLANTUML_LIMIT_SIZE` environment variable.*
## DBML
The [DBML](https://www.dbml.org/) format is more limited than DOT and PlantUML, as it focuses on tables only.
Multiple schemas that use the same table are merged into one. Embedded schemas
cannot be displayed.
`TableGroup`s are supported and can be set via the `:map_node` option using `Ecto.ERD.Node.set_cluster/2`.
This format is handy if you use [dbdiagram.io](https://dbdiagram.io) or [dbdocs.io](https://dbdocs.io).
```
$ mix ecto.gen.erd --output-path=ecto_erd.dbml
```
## QuickDBD
A format used by [QuickDBD](https://www.quickdatabasediagrams.com), a competitor of [dbdiagram.io](https://dbdiagram.io).
Like DBML, it focuses on tables and cannot display embedded schemas. However, this format doesn't support clusters.
It doesn't have a reserved file extension, but we use `*.qdbd`.
```
$ mix ecto.gen.erd --output-path=ecto_erd.qdbd
```
## Mermaid
[Mermaid](https://mermaid-js.github.io/mermaid/#/entityRelationshipDiagram) is also limited compared to [DOT](#module-dot) and [PlantUML](#module-plantuml) and focuses on tables only.
Multiple schemas that use the same table are merged into one. Embedded schemas cannot be displayed.
Clusters are not supported.
The benefit of this format is that it is [supported by GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/).
```
$ mix ecto.gen.erd --output-path=ecto_erd.mmd
```
If you have installed [mermaid-cli](https://github.com/mermaid-js/mermaid-cli) locally, you can convert the output `*.mmd` file to an image using the following command:
```
$ mmdc -i ecto_erd.mmd -o erd.png -w 6000 -H 6000
```
The default `-w` and `-H` values are small (`800` and `600`, respectively), so it's better to provide larger values from the start.
## Command line options
* `--output-path` - path to the output file. Defaults to `ecto_erd.dot`. Supported file extensions: `dot`, `puml`, `dbml`, `qdbd`.
* `--config-path` - path to the config file. Defaults to `.ecto_erd.exs`.
## Configuration file
When you run the `mix ecto.gen.erd` task, it tries to read configuration from the `.ecto_erd.exs` file in the current
working directory. The configuration file must return a keyword list.
### Options
* `:fontname` - font name. Defaults to `Roboto Mono`. Must be a monospaced font if the output format is `dot` and more than one column is displayed.
Supported only for `dot` and `puml` files.
* `:columns` - list of columns displayed for each node (schema/source). Set to `[]` to hide fields completely.
Available columns: `:name`, `:type`. Supported for `dot`, `puml`, and `mmd` (`mmd` allows only `[]` or the default value).
Default values:
* `[:name, :type]` for `dot` and `puml`
* `[:type, :name]` for `mmd`
* `:map_node` - a function that removes a node from the diagram or assigns it to a cluster. Defaults to `Function.identity/1`,
which means all nodes are displayed outside any cluster by default.
Use `Ecto.ERD.Node.set_cluster/2` in this function to set a cluster. Supported only by [DOT](#module-dot), [PlantUML](#module-plantuml),
and [DBML](#module-dbml).
Return `nil` to remove a node from the diagram.
* `:otp_app` - the application to scan (along with its dependencies) to collect Ecto schemas.
Defaults to `Mix.Project.config()[:app]`. Configure this only when running the task from an umbrella root.
A configuration file with default values for `dot` and `puml` can look like this:
# .ecto_erd.exs
[
fontname: "Roboto Mono",
columns: [:name, :type],
map_node: &Function.identity/1,
otp_app: Mix.Project.config()[:app]
]
"""
@shortdoc "Generate an ERD"
use Mix.Task
@requirements ["app.config"]
@impl true
def run(args) do
{cli_opts, _} =
OptionParser.parse!(args, strict: [output_path: :string, config_path: :string])
config_path = Keyword.get(cli_opts, :config_path, ".ecto_erd.exs")
file_opts =
if File.exists?(config_path) do
{file_opts, _} = Code.eval_file(config_path)
file_opts
else
[]
end
otp_app =
cond do
Keyword.has_key?(file_opts, :otp_app) -> file_opts[:otp_app]
not is_nil(Mix.Project.config()[:app]) -> Mix.Project.config()[:app]
true -> raise "Unable to detect `:otp_app`, please specify it explicitly"
end
output_path = cli_opts[:output_path] || file_opts[:output_path] || "ecto_erd.dot"
map_node_callback = file_opts[:map_node] || (&Function.identity/1)
output =
otp_app
|> Ecto.ERD.SchemaModules.scan()
|> Ecto.ERD.Document.render(
Path.extname(output_path),
map_node_callback,
Keyword.take(file_opts, [:fontname, :columns])
)
File.write!(output_path, output)
end
end
================================================
FILE: mix.exs
================================================
# examples_generator.exs is needed only for docs and is not shipped in package
if File.exists?("examples_generator.exs") do
Code.compile_file("examples_generator.exs")
else
defmodule Ecto.ERD.ExamplesGenerator do
def projects, do: []
def run(_), do: :noop
end
end
defmodule Ecto.ERD.MixProject do
use Mix.Project
@source_url "https://github.com/fuelen/ecto_erd/"
@version "0.6.6"
def project do
[
app: :ecto_erd,
version: @version,
elixir: "~> 1.12",
start_permanent: Mix.env() == :prod,
deps: deps(),
package: package(),
description: description(),
name: "Ecto ERD",
docs: docs(),
aliases: [docs: [&generate_examples/1, "docs"]],
dialyzer: [plt_add_apps: [:mix]]
]
end
defp description do
"ERD generator for Ecto users"
end
defp docs do
[
main: "Mix.Tasks.Ecto.Gen.Erd",
extras:
Enum.map(Ecto.ERD.ExamplesGenerator.projects(), fn project ->
{:"tmp/docs/#{project}.md", [title: project]}
end),
source_url: @source_url,
source_ref: "v#{@version}",
groups_for_extras: [
Examples: ~r"tmp/docs/"
]
]
end
defp package do
[
licenses: ["Apache-2.0"],
links: %{
GitHub: @source_url
}
]
end
def application do
[
extra_applications: [:logger]
]
end
defp generate_examples(_) do
run? = "Generate examples? y/n: " |> IO.gets() |> String.trim() |> String.downcase() == "y"
if run? do
Ecto.ERD.ExamplesGenerator.run(Path.join([@source_url, "blob", "v#{@version}"]))
end
end
defp deps do
[
{:ex_doc, "~> 0.24", only: :dev, runtime: false},
{:html_entities, "~> 0.5"},
{:ecto, "~> 3.12"},
{:dialyxir, "~> 1.0", only: [:dev], runtime: false}
]
end
end
================================================
FILE: test/ecto/erd_test.exs
================================================
defmodule Ecto.ERDTest do
use ExUnit.Case
alias Ecto.ERD.{Node, Field, Graph}
alias Ecto.ERD.Document.{DBML, Dot}
test inspect(&DBML.enums_mapping/1) do
result =
[
%Node{
source: "credentials",
fields: [
Field.new(%{
name: :flow,
type: {:parameterized, {Ecto.Enum, Ecto.Enum.init(values: [:simple, :complex])}}
})
]
},
%Node{
source: "invitations",
fields: [
Field.new(%{
name: :flow,
type: {:parameterized, {Ecto.Enum, Ecto.Enum.init(values: [:simple, :complex])}}
})
]
},
%Node{
source: "users",
fields: [
Field.new(%{
name: :status,
type:
{:parameterized,
{Ecto.Enum, Ecto.Enum.init(values: [:active, :suspended, :invited])}}
})
]
},
%Node{
source: "admins",
fields: [
Field.new(%{
name: :status,
type: {:parameterized, {Ecto.Enum, Ecto.Enum.init(values: [:active, :suspended])}}
})
]
},
%Node{
source: "projects",
fields: [
Field.new(%{
name: :status,
type: {:parameterized, {Ecto.Enum, Ecto.Enum.init(values: [:live, :closed])}}
})
]
}
]
|> DBML.enums_mapping()
assert result == %{
["admins", :status] => {"admins_status", ["active", "suspended"]},
["credentials", :flow] => {"flow", ["complex", "simple"]},
["invitations", :flow] => {"flow", ["complex", "simple"]},
["projects", :status] => {"projects_status", ["closed", "live"]},
["users", :status] => {"users_status", ["active", "invited", "suspended"]}
}
end
test "DOT format renders primary key indicator" do
graph = %Graph{
nodes: [
%Node{
source: "users",
schema_module: MyApp.User,
fields: [
Field.new(%{name: :id, type: :integer, primary?: true}),
Field.new(%{name: :email, type: :string, primary?: false}),
Field.new(%{name: :name, type: :string, primary?: false})
]
}
],
edges: []
}
result = Dot.render(graph, [])
# Verify that the primary key field is bold
assert result =~ ~r/:id\s*<\/b>/
# Verify that non-primary fields are not bold
refute result =~ ~r/:email\s*<\/b>/
refute result =~ ~r/:name\s*<\/b>/
end
end
================================================
FILE: test/test_helper.exs
================================================
ExUnit.start()