Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add enum support #26

Merged
merged 5 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ test-all:
test-unit:
go test --short $(test_target) -cover -json | tparse -all

.PHONY: test-cleanup
test-cleanup:
go clean -testcache

.PHONY: publish-package
publish-package:
GOPROXY=proxy.golang.org go list -m github.com/KarnerTh/mermerd@$(GIT_TAG)
GOPROXY=proxy.golang.org go list -m github.com/KarnerTh/mermerd@$(GIT_TAG)
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (after version 0.0.5).

## [0.5.0] - 2022-12-xx
### Added
- Support enum description ([Issue #15](https://github.com/KarnerTh/mermerd/issues/15))

## [0.4.1] - 2022-09-28
### Fixed
- Fix wrong column format for `is_primary` ([Issue #24](https://github.com/KarnerTh/mermerd/issues/24))
Expand Down Expand Up @@ -95,6 +99,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (after version 0.0
### Added
- Initial release of mermerd

[0.5.0]: https://github.com/KarnerTh/mermerd/releases/tag/v0.5.0

[0.4.1]: https://github.com/KarnerTh/mermerd/releases/tag/v0.4.1

[0.4.0]: https://github.com/KarnerTh/mermerd/releases/tag/v0.4.0
Expand Down
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func init() {
rootCmd.Flags().Bool(config.DebugKey, false, "show debug logs")
rootCmd.Flags().Bool(config.OmitConstraintLabelsKey, false, "omit the constraint labels")
rootCmd.Flags().Bool(config.OmitAttributeKeysKey, false, "omit the attribute keys (PK, FK)")
rootCmd.Flags().Bool(config.ShowEnumValuesKey, false, "show enum values in description column")
rootCmd.Flags().BoolP(config.EncloseWithMermaidBackticksKey, "e", false, "enclose output with mermaid backticks (needed for e.g. in markdown viewer)")
rootCmd.Flags().StringP(config.ConnectionStringKey, "c", "", "connection string that should be used")
rootCmd.Flags().StringP(config.SchemaKey, "s", "", "schema that should be used")
Expand All @@ -84,7 +85,7 @@ func init() {
bindFlagToViper(config.SchemaKey)
bindFlagToViper(config.OutputFileNameKey)
bindFlagToViper(config.SelectedTablesKey)

bindFlagToViper(config.ShowEnumValuesKey)
}

func bindFlagToViper(key string) {
Expand Down
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
DebugKey = "debug"
OmitConstraintLabelsKey = "omitConstraintLabels"
OmitAttributeKeysKey = "omitAttributeKeys"
ShowEnumValuesKey = "showEnumValues"
)

type config struct{}
Expand All @@ -30,6 +31,7 @@ type MermerdConfig interface {
Debug() bool
OmitConstraintLabels() bool
OmitAttributeKeys() bool
ShowEnumValues() bool
}

func NewConfig() MermerdConfig {
Expand Down Expand Up @@ -79,3 +81,7 @@ func (c config) OmitConstraintLabels() bool {
func (c config) OmitAttributeKeys() bool {
return viper.GetBool(OmitAttributeKeysKey)
}

func (c config) ShowEnumValues() bool {
return viper.GetBool(ShowEnumValuesKey)
}
24 changes: 18 additions & 6 deletions database/database_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ type columnTestResult struct {
isForeign bool
}

type connectionParameter struct {
connectionString string
schema string
}

var (
testConnectionPostgres connectionParameter = connectionParameter{connectionString: "postgresql://user:password@localhost:5432/mermerd_test", schema: "public"}
testConnectionMySql connectionParameter = connectionParameter{connectionString: "mysql://user:password@tcp(127.0.0.1:3306)/mermerd_test", schema: "mermerd_test"}
testConnectionMsSql connectionParameter = connectionParameter{connectionString: "sqlserver://sa:securePassword1!@localhost:1433?database=mermerd_test", schema: "dbo"}
)

func TestDatabaseIntegrations(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
Expand All @@ -27,18 +38,18 @@ func TestDatabaseIntegrations(t *testing.T) {
}{
{
dbType: Postgres,
connectionString: "postgresql://user:password@localhost:5432/mermerd_test",
schema: "public",
connectionString: testConnectionPostgres.connectionString,
schema: testConnectionPostgres.schema,
},
{
dbType: MySql,
connectionString: "mysql://user:password@tcp(127.0.0.1:3306)/mermerd_test",
schema: "mermerd_test",
connectionString: testConnectionMySql.connectionString,
schema: testConnectionMySql.schema,
},
{
dbType: MsSql,
connectionString: "sqlserver://sa:securePassword1!@localhost:1433?database=mermerd_test",
schema: "dbo",
connectionString: testConnectionMsSql.connectionString,
schema: testConnectionMsSql.schema,
},
}

Expand Down Expand Up @@ -95,6 +106,7 @@ func TestDatabaseIntegrations(t *testing.T) {
"article_label",
"test_1_a",
"test_1_b",
"test_2_enum",
}
assert.Nil(t, err)
assert.ElementsMatch(t, expectedResult, tables)
Expand Down
5 changes: 3 additions & 2 deletions database/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ func (c *mySqlConnector) GetColumns(tableName string) ([]ColumnResult, error) {
left join information_schema.table_constraints tc on tc.constraint_name = cu.constraint_name
where cu.column_name = c.column_name
and cu.table_name = c.table_name
and tc.constraint_type = 'FOREIGN KEY') as is_foreign
and tc.constraint_type = 'FOREIGN KEY') as is_foreign,
case when c.data_type = 'enum' then REPLACE(REPLACE(REPLACE(REPLACE(c.column_type, 'enum', ''), '\'', ''), '(', ''), ')', '') else '' end as enum_values
from information_schema.columns c
where c.table_name = ?
order by c.ordinal_position;
Expand All @@ -103,7 +104,7 @@ func (c *mySqlConnector) GetColumns(tableName string) ([]ColumnResult, error) {
var columns []ColumnResult
for rows.Next() {
var column ColumnResult
if err = rows.Scan(&column.Name, &column.DataType, &column.IsPrimary, &column.IsForeign); err != nil {
if err = rows.Scan(&column.Name, &column.DataType, &column.IsPrimary, &column.IsForeign, &column.EnumValues); err != nil {
return nil, err
}

Expand Down
35 changes: 35 additions & 0 deletions database/mysql_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package database

import (
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

func TestMysqlEnums(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}

// Arrange
var enumValues string

// Act
connector, _ := NewConnectorFactory().NewConnector(testConnectionMySql.connectionString)
if err := connector.Connect(); err != nil {
logrus.Error(err)
t.FailNow()
}
columns, err := connector.GetColumns("test_2_enum")

// Assert
for _, column := range columns {
if column.Name == "fruit" {
enumValues = column.EnumValues
}
}

assert.Nil(t, err)
assert.Equal(t, "apple,banana", enumValues)
}
48 changes: 30 additions & 18 deletions database/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,35 @@ func (c *postgresConnector) GetTables(schemaName string) ([]string, error) {

func (c *postgresConnector) GetColumns(tableName string) ([]ColumnResult, error) {
rows, err := c.db.Query(`
select c.column_name,
c.data_type,
(select count(*) > 0
from information_schema.key_column_usage cu
left join information_schema.table_constraints tc on tc.constraint_name = cu.constraint_name
where cu.column_name = c.column_name
and cu.table_name = c.table_name
and tc.constraint_type = 'PRIMARY KEY') as is_primary,
(select count(*) > 0
from information_schema.key_column_usage cu
left join information_schema.table_constraints tc on tc.constraint_name = cu.constraint_name
where cu.column_name = c.column_name
and cu.table_name = c.table_name
and tc.constraint_type = 'FOREIGN KEY') as is_foreign
from information_schema.columns c
where c.table_name = $1
order by c.ordinal_position;
select c.column_name,
(case
when c.data_type = 'USER-DEFINED'
then c.udt_name
else c.data_type
end) as data_type,
(select count(*) > 0
from information_schema.key_column_usage cu
left join information_schema.table_constraints tc on tc.constraint_name = cu.constraint_name
where cu.column_name = c.column_name
and cu.table_name = c.table_name
and tc.constraint_type = 'PRIMARY KEY') as is_primary,
(select count(*) > 0
from information_schema.key_column_usage cu
left join information_schema.table_constraints tc on tc.constraint_name = cu.constraint_name
where cu.column_name = c.column_name
and cu.table_name = c.table_name
and tc.constraint_type = 'FOREIGN KEY') as is_foreign,
coalesce(string_agg(enumlabel, ',' order by enumsortorder), '') as enum_values
from information_schema.columns c
left join pg_type typ on c.udt_name = typ.typname
left join pg_enum enu on typ.oid = enu.enumtypid
where c.table_name = $1
group by c.column_name,
c.table_name,
c.data_type,
c.udt_name,
c.ordinal_position
order by c.ordinal_position;
`, tableName)
if err != nil {
return nil, err
Expand All @@ -104,7 +116,7 @@ func (c *postgresConnector) GetColumns(tableName string) ([]ColumnResult, error)
var columns []ColumnResult
for rows.Next() {
var column ColumnResult
if err = rows.Scan(&column.Name, &column.DataType, &column.IsPrimary, &column.IsForeign); err != nil {
if err = rows.Scan(&column.Name, &column.DataType, &column.IsPrimary, &column.IsForeign, &column.EnumValues); err != nil {
return nil, err
}

Expand Down
35 changes: 35 additions & 0 deletions database/postgres_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package database

import (
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

func TestPostgresEnums(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}

// Arrange
var enumValues string

// Act
connector, _ := NewConnectorFactory().NewConnector(testConnectionPostgres.connectionString)
if err := connector.Connect(); err != nil {
logrus.Error(err)
t.FailNow()
}
columns, err := connector.GetColumns("test_2_enum")

// Assert
for _, column := range columns {
if column.Name == "fruit" {
enumValues = column.EnumValues
}
}

assert.Nil(t, err)
assert.Equal(t, "apple,banana", enumValues)
}
9 changes: 5 additions & 4 deletions database/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ type TableResult struct {
}

type ColumnResult struct {
Name string
DataType string
IsPrimary bool
IsForeign bool
Name string
DataType string
IsPrimary bool
IsForeign bool
EnumValues string
}

type ConstraintResultList []ConstraintResult
Expand Down
6 changes: 6 additions & 0 deletions diagram/diagram.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,15 @@ func (d diagram) Create(result *database.Result) error {
attributeKey = none
}

var enumValues string
if d.config.ShowEnumValues() {
enumValues = column.EnumValues
}

columnData[columnIndex] = ErdColumnData{
Name: column.Name,
DataType: column.DataType,
EnumValues: enumValues,
AttributeKey: attributeKey,
}
}
Expand Down
1 change: 1 addition & 0 deletions diagram/diagram_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ErdTableData struct {
type ErdColumnData struct {
Name string
DataType string
EnumValues string
AttributeKey ErdAttributeKey
}

Expand Down
2 changes: 1 addition & 1 deletion diagram/erd_template.gommd
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ erDiagram
{{- range .Tables}}
{{.Name}} {
{{- range .Columns}}
{{.DataType}} {{.Name}} {{.AttributeKey}}
{{.DataType}} {{.Name}} {{.AttributeKey}} {{- if .EnumValues}}"{{.EnumValues}}"{{end -}}
{{- end}}
}
{{end -}}
Expand Down
1 change: 1 addition & 0 deletions exampleRunConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ encloseWithMermaidBackticks: false
debug: false
omitConstraintLabels: false
omitAttributeKeys: false
showEnumValues: false
14 changes: 14 additions & 0 deletions mocks/MermerdConfig.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ for your operating system. To be able to use it globally on your system, add the
* Use it in CI/CD pipeline via a run configuration
* Either generate plain mermaid syntax or enclose it with mermaid backticks to use directly in e.g. GitHub markdown
* Show primary and foreign keys
* Show enum values of enum column

## Why would I need it / Why should I care?

Expand Down Expand Up @@ -78,6 +79,7 @@ via `mermerd -h`
-s, --schema string schema that should be used
--selectedTables strings tables to include
--showAllConstraints show all constraints, even though the table of the resulting constraint was not selected
--showEnumValues show enum values in description column
--useAllTables use all available tables
```

Expand All @@ -98,6 +100,7 @@ outputFileName: "my-db.mmd"
debug: false
omitConstraintLabels: false
omitAttributeKeys: false
showEnumValues: false

# These connection strings are available as suggestions in the cli (use tab to access)
connectionStringSuggestions:
Expand Down Expand Up @@ -130,6 +133,7 @@ outputFileName: "my-db.mmd"
debug: true
omitConstraintLabels: true
omitAttributeKeys: true
showEnumValues: true
```

## Example usages
Expand Down
Loading