Skip to content

Commit

Permalink
Create DOT generator
Browse files Browse the repository at this point in the history
  • Loading branch information
loicknuchel committed Oct 8, 2024
1 parent 037591d commit 69ef4fd
Show file tree
Hide file tree
Showing 15 changed files with 345 additions and 27 deletions.
9 changes: 5 additions & 4 deletions backend/lib/azimutt.ex
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ defmodule Azimutt do
project_share: %{name: "Sharing project", free: false, solo: false, team: false, enterprise: true, pro: true, description: "Use private links & embed to share with guest."},
api: %{name: "API access", free: false, solo: false, team: false, enterprise: true, pro: true, description: "Fetch and update sources and documentation programmatically."},
sso: %{name: "SSO", free: false, solo: false, team: false, enterprise: true, pro: false, description: "Soon..."},
user_rights: %{name: "User rights", free: false, solo: false, team: false, enterprise: true, pro: false, description: "Soon... Have read-only users in your organization."},
gateway_custom: %{name: "Custom gateway", free: false, solo: false, team: false, enterprise: true, pro: false, description: "Soon... Securely connect to your databases."},
user_rights: %{name: "User rights", free: false, solo: false, team: false, enterprise: true, pro: false},
gateway_custom: %{name: "Custom gateway", free: false, solo: false, team: false, enterprise: true, pro: false},
billing: %{name: "Flexible billing", free: false, solo: false, team: false, enterprise: true, pro: false},
support_on_premise: %{name: "On-premise support", free: false, solo: false, team: false, enterprise: true, pro: false},
support_enterprise: %{name: "Enterprise support", free: false, solo: false, team: false, enterprise: true, pro: false, description: "Priority email, answer within 48h."},
Expand Down Expand Up @@ -321,6 +321,7 @@ defmodule Azimutt do
%{id: "mongodb", name: "MongoDB", parse: false, generate: false},
%{id: "mariadb", name: "MariaDB", parse: false, generate: false},
%{id: "prisma", name: "Prisma", parse: false, generate: false},
%{id: "dot", name: "DOT", parse: false, generate: true},
%{id: "mermaid", name: "Mermaid", parse: false, generate: true},
%{id: "quicksql", name: "Quick SQL", parse: false, generate: false},
%{id: "markdown", name: "Markdown", parse: false, generate: true},
Expand Down Expand Up @@ -399,8 +400,8 @@ defmodule Azimutt do
%{path: ["converters"], name: "Converters"}
]
},
%{path: ["installation"], name: "Installation"},
%{path: ["data-privacy"], name: "Data privacy", details: "how Azimutt keep your data safe"}
%{path: ["data-privacy"], name: "Data privacy", details: "how Azimutt keep your data safe"},
%{path: ["installation"], name: "Installation"}
]
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- Hey! Nice to meet you ❤️-->
<!-- Did you know Azimutt is an Open Source project written in Elm & Elixir? -->
<!-- You can find it here: <%= Azimutt.config(:azimutt_github) %> -->
<!-- While you are here, please take the time to give us a ⭐️, it's a nice support for us. -->
<!-- You can also find us on <%= Azimutt.config(:azimutt_twitter) %> or <%= Azimutt.config(:azimutt_linkedin) %> -->
<!-- Come and say hi 👋, it keeps our motivation on top! -->
<!-- Hey! Nice to meet you ❤️ -->
<!-- Did you know Azimutt is an Open Source project written in Elm & Elixir? -->
<!-- You can find it here: <%= Azimutt.config(:azimutt_github) %> -->
<!-- While you are here, please take the time to give us a ⭐️, it's a nice support for us. -->
<!-- You can also find us on <%= Azimutt.config(:azimutt_twitter) %> or <%= Azimutt.config(:azimutt_linkedin) %> -->
<!-- Come and say hi 👋, it keeps our motivation on top! -->
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
window.gateway_url = '<%= org_gateway || cc_gateway || Azimutt.config(:gateway_url) %>'
window.role = '<%= member && member.role || :owner %>'
window.params = {}<%= for param <- ["database", "sql", "prisma", "json", "aml", "empty", "project", "sample", "name", "storage"] |> Enum.filter(fn p -> @conn.params[p] end) do %>
window.params.<%= param %> = '<%= @conn.params[param] %>'<% end %>
window.params.<%= param %> = `<%= raw(@conn.params[param] |> String.replace("`", "\\`")) %>`<% end %>
</script>
<%= if Azimutt.config(:sentry_frontend_dsn) do %>
<script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= cond do %>
<% @converter == "aml" || @converter == "amlv1" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/aml/src/amlGenerator.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "dot" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/aml/src/dotGenerator.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "mermaid" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/aml/src/mermaidGenerator.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "markdown" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/aml/src/markdownGenerator.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "postgres" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/parser-sql/src/postgresGenerator.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "json" -> %>
<br><br>The JSON format is defined here,
you can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/models/src/database.ts" target="_blank" rel="noopener noreferrer" class="underline">suggest improvements</a>.
<% true -> %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<%= cond do %>
<% @converter == "aml" || @converter == "amlv1" -> %>
<br><br>Have any issue?
You can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/aml/src/amlParser.ts" target="_blank" rel="noopener noreferrer" class="underline">update it here</a>.
<% @converter == "json" -> %>
<br><br>The JSON format is defined here,
you can <a href="https://github.com/azimuttapp/azimutt/blob/main/libs/models/src/database.ts" target="_blank" rel="noopener noreferrer" class="underline">suggest improvements</a>.
<% true -> %>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
<% @converter == "prisma" -> %>
A <a href="https://www.prisma.io" target="_blank" rel="noopener noreferrer" class="underline">Node.jS and TypeScript ORM</a> that use a <a href="https://www.prisma.io/docs/orm/prisma-schema" target="_blank" rel="noopener noreferrer" class="underline">dedicated schema syntax</a> to define its data model.
It can convert this schema to several databases and pull or push it to them.
<% @converter == "dot" -> %>
<a href="https://graphviz.org" target="_blank" rel="noopener noreferrer" class="underline">Graphviz</a> is an open source graph visualization software with a language, named DOT, to define the diagrams.
It has many visualization options and plugins.
<% @converter == "mermaid" -> %>
It's a <a href="https://mermaid.js.org" target="_blank" rel="noopener noreferrer" class="underline">Markdown-inspired text definitions</a> that generate flowcharts diagrams.
It can be used to generate Entity-Relationship diagrams, and even has a <a href="https://mermaid.live" target="_blank" rel="noopener noreferrer" class="underline">Live Editor</a>.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script src="https://cdn.jsdelivr.net/npm/@azimutt/[email protected].5/out/bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@azimutt/[email protected].6/out/bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@azimutt/[email protected]/out/bundle.min.js"></script>
<!--<script src="/elm/aml.min.js"></script>-->
<!--<script src="/elm/sql.min.js"></script>-->
Expand Down Expand Up @@ -86,23 +86,33 @@
function monacoLang(lang) {
if (lang === 'amlv1') return 'aml'
if (lang === 'postgres') return 'pgsql'
if (lang === 'dot') return 'plaintext'
if (lang === 'mermaid') return 'plaintext'
return lang
}
function parse(lang, content) {
if (lang === 'aml') return aml.parseAml(content)
if (lang === 'amlv1') return aml.parseAml(content).mapError(errs => errs.filter(e => e.kind !== 'LegacySyntax'))
if (lang === 'json') return aml.parseJsonDatabase(content)
return {errors: [{message: 'Unsupported source dialect: ' + lang, kind: 'UnsupportedDialect', level: 'error', offset: {start: 0, end: 100}, position: {start: {line: 1, column: 1}, end: {line: 10, column: 10}}}]}
try {
if (lang === 'aml') return aml.parseAml(content)
if (lang === 'amlv1') return aml.parseAml(content).mapError(errs => errs.filter(e => e.kind !== 'LegacySyntax'))
if (lang === 'json') return aml.parseJsonDatabase(content)
return {errors: [{message: 'Unsupported source dialect: ' + lang, kind: 'UnsupportedDialect', level: 'error', offset: {start: 0, end: 100}, position: {start: {line: 1, column: 1}, end: {line: 10, column: 10}}}]}
} catch (e) {
return {errors: [{message: 'Failed to parse ' + lang + (e && e.message ? ': ' + e.message : ''), kind: 'DialectError', level: 'error', offset: {start: 0, end: 100}, position: {start: {line: 1, column: 1}, end: {line: 10, column: 10}}}]}
}
}
function format(lang, db) {
if (lang === 'aml') return aml.generateAml(db)
if (lang === 'amlv1') return aml.generateAml(db, true)
if (lang === 'json') return aml.generateJsonDatabase(db)
if (lang === 'postgres') return sql.generateSql(db, 'postgres')
if (lang === 'mermaid') return aml.generateMermaid(db)
if (lang === 'markdown') return aml.generateMarkdown(db)
return 'Unsupported destination dialect: ' + lang
try {
if (lang === 'aml') return aml.generateAml(db)
if (lang === 'amlv1') return aml.generateAml(db, true)
if (lang === 'json') return aml.generateJsonDatabase(db)
if (lang === 'postgres') return sql.generateSql(db, 'postgres')
if (lang === 'dot') return aml.generateDot(db)
if (lang === 'mermaid') return aml.generateMermaid(db)
if (lang === 'markdown') return aml.generateMarkdown(db)
return 'Unsupported destination dialect: ' + lang
} catch (e) {
return 'Failed to generate ' + lang + (e && e.message ? ': ' + e.message : '')
}
}
function getDefaultValue(lang) {
// default value from: `value` query param, or url hash, or local storage, or hardcoded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,17 @@
<div class="mt-6 flex justify-around justify-items-center">
<div class="w-1/3">
<div class="text-base font-semibold leading-7 text-gray-900"><%= @from.name %></div>
<p class="mt-3 text-base leading-7 text-gray-600"><%= render "converters/_description-short.html", conn: @conn, converter: @from.id %></p>
<p class="mt-3 text-base leading-7 text-gray-600">
<%= render "converters/_description-short.html", conn: @conn, converter: @from.id %>
<%= render "converters/_description-parse.html", conn: @conn, converter: @from.id %>
</p>
</div>
<div class="w-1/3">
<div class="text-base font-semibold leading-7 text-gray-900"><%= @to.name %></div>
<p class="mt-3 text-base leading-7 text-gray-600"><%= render "converters/_description-short.html", conn: @conn, converter: @to.id %></p>
<p class="mt-3 text-base leading-7 text-gray-600">
<%= render "converters/_description-short.html", conn: @conn, converter: @to.id %>
<%= render "converters/_description-generate.html", conn: @conn, converter: @to.id %>
</p>
</div>
</div>

Expand Down
Binary file added backend/priv/static/images/converters/dot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions libs/aml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ You can have both, a `result` and some `errors` as there is syntax recovery and
This package also offer a few more features:

```typescript
function generateDot(database: Database) {} // generate a DOT graph
function generateMermaid(database: Database) {} // generate a Mermaid erDiagram
function generateMarkdown(database: Database) {} // generate markdown documentation
function generateJsonDatabase(database: Database): string {} // generate nice JSON (similar to `JSON.stringify(db, null, 2), but more compact)
Expand Down
2 changes: 1 addition & 1 deletion libs/aml/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azimutt/aml",
"version": "0.1.5",
"version": "0.1.6",
"description": "Parse and Generate AML: the easiest language to define database schema.",
"keywords": ["DSL", "language", "database", "parser"],
"homepage": "https://azimutt.app/aml",
Expand Down
117 changes: 117 additions & 0 deletions libs/aml/resources/full.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
digraph {
node [shape=none, margin=0]

users [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">users</td></tr>
<tr><td align="left">id</td><td align="left">uid</td><td align="left">pk</td></tr>
<tr><td align="left">first_name</td><td align="left">varchar</td><td align="left">unique</td></tr>
<tr><td align="left">last_name</td><td align="left">varchar</td><td align="left">unique</td></tr>
<tr><td align="left">email</td><td align="left">varchar</td><td align="left">unique</td></tr>
<tr><td align="left">is_admin</td><td align="left">bool</td><td align="left"></td></tr>
</table>
>]

"cms.posts" [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">cms.posts</td></tr>
<tr><td align="left">id</td><td align="left">int</td><td align="left">pk</td></tr>
<tr><td align="left">title</td><td align="left">varchar(100)</td><td align="left">unique</td></tr>
<tr><td align="left">status</td><td align="left">post_status</td><td align="left"></td></tr>
<tr><td align="left">content</td><td align="left">varchar</td><td align="left"></td></tr>
<tr><td align="left">settings</td><td align="left">json</td><td align="left"></td></tr>
<tr><td align="left">created_at</td><td align="left">timestamp with time zone</td><td align="left"></td></tr>
<tr><td align="left">created_by</td><td align="left">int</td><td align="left">fk</td></tr>
</table>
>]
"cms.posts" -> users [label=settings.publish_by]
"cms.posts" -> users [label=created_by]

post_members [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">post_members</td></tr>
<tr><td align="left">post_id</td><td align="left">uuid</td><td align="left">pk, fk</td></tr>
<tr><td align="left">user_id</td><td align="left">int</td><td align="left">pk, fk</td></tr>
<tr><td align="left">role</td><td align="left">varchar(10)</td><td align="left"></td></tr>
</table>
>]
post_members -> "cms.posts" [label=post_id]
post_members -> users [label=user_id]

"legacy schema.post member details" [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">legacy schema.post member details</td></tr>
<tr><td align="left">post_id</td><td align="left">uuid</td><td align="left">pk, fk</td></tr>
<tr><td align="left">user_id</td><td align="left">int</td><td align="left">pk, fk</td></tr>
<tr><td align="left">index</td><td align="left">int</td><td align="left"></td></tr>
<tr><td align="left">added by</td><td align="left">int</td><td align="left">fk</td></tr>
</table>
>]
"legacy schema.post member details" -> users [label=added by]
"legacy schema.post member details" -> post_members [label=post_id,user_id]

comments [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">comments</td></tr>
<tr><td align="left">id</td><td align="left">uuid</td><td align="left">pk</td></tr>
<tr><td align="left">item_kind</td><td align="left">comment_item</td><td align="left">index</td></tr>
<tr><td align="left">item_id</td><td align="left">int</td><td align="left">fk, index</td></tr>
<tr><td align="left">content</td><td align="left">unknown</td><td align="left"></td></tr>
<tr><td align="left">created_by</td><td align="left">unknown</td><td align="left">fk</td></tr>
</table>
>]
comments -> users [label=created_by]
comments -> users [label=item_id]
comments -> "cms.posts" [label=item_id]

"db1.web.public.legacy_slug" [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">db1.web.public.legacy_slug</td></tr>
<tr><td align="left">old_slug</td><td align="left">slug</td><td align="left"></td></tr>
<tr><td align="left">new_slug</td><td align="left">slug</td><td align="left"></td></tr>
<tr><td align="left">cur_slug</td><td align="left">varchar</td><td align="left">fk</td></tr>
</table>
>]
"db1.web.public.legacy_slug" -> "cms.posts" [label=cur_slug]

organizations [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">organizations</td></tr>
<tr><td align="left">id</td><td align="left">int</td><td align="left">pk, fk</td></tr>
<tr><td align="left">name</td><td align="left">varchar(50)</td><td align="left"></td></tr>
<tr><td align="left">content</td><td align="left">box</td><td align="left"></td></tr>
</table>
>]
organizations -> users [label=id]

"identity...profiles" [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">identity...profiles</td></tr>
<tr><td align="left">id</td><td align="left">int</td><td align="left">pk, fk</td></tr>
</table>
>]
"identity...profiles" -> users [label=id]

admins [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">admins</td></tr>
<tr><td align="left">id</td><td align="left">unknown</td><td align="left"></td></tr>
<tr><td align="left">first_name</td><td align="left">unknown</td><td align="left"></td></tr>
<tr><td align="left">last_name</td><td align="left">unknown</td><td align="left"></td></tr>
<tr><td align="left">email</td><td align="left">unknown</td><td align="left"></td></tr>
</table>
>]

guests [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">guests</td></tr>
</table>
>]

"social..social_accounts" [label=<
<table border="0" cellborder="1" cellspacing="0" cellpadding="4">
<tr><td bgcolor="lightblue" colspan="3">social..social_accounts</td></tr>
</table>
>]
"social..social_accounts" -> users
}
Loading

0 comments on commit 69ef4fd

Please sign in to comment.