Skip to content

Commit

Permalink
Merge pull request #75 from k1LoW/add-lint
Browse files Browse the repository at this point in the history
Add `tbls lint`
  • Loading branch information
k1LoW authored Feb 23, 2019
2 parents b7e5cbb + fe75072 commit 33ea1f6
Show file tree
Hide file tree
Showing 66 changed files with 954 additions and 171 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Available Commands:
diff diff database and document
doc document a database
help Help about any command
lint check database document
out analyzes a database and output
version print tbls version

Expand Down Expand Up @@ -113,11 +114,11 @@ $ env TBLS_DSN=my://root:mypass@localhost:33306/testdb TBLS_DOC_PATH=sample/mysq

## Add additional data (relations, comments) to schema

To add additional data to the schema, specify [the YAML file](testdata/additional_data.yml) with the `--config` option as follows
To add additional data to the schema, add settings to `.tbls.yml` or `--config` like [YAML file](testdata/additional_data.yml) (`relations`, `comments`)

``` console
$ tbls doc mysql://user:pass@hostname:3306/dbname --config path/to/additional_data.yml ./dbdoc
```
## Lint database document

To check database document, add settings to `.tbls.yml` or `--config` like [YAML file](testdata/additional_data.yml) (`lint`)

## Installation

Expand Down
2 changes: 1 addition & 1 deletion cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ var diffCmd = &cobra.Command{
os.Exit(1)
}

err = s.LoadAdditionalData(c)
err = c.MergeAdditionalData(s)
if err != nil {
printError(err)
os.Exit(1)
Expand Down
2 changes: 1 addition & 1 deletion cmd/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var docCmd = &cobra.Command{
os.Exit(1)
}

err = s.LoadAdditionalData(c)
err = c.MergeAdditionalData(s)
if err != nil {
printError(err)
os.Exit(1)
Expand Down
95 changes: 95 additions & 0 deletions cmd/lint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright © 2019 Ken'ichiro Oyama <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cmd

import (
"fmt"
"os"
"reflect"

"github.com/k1LoW/tbls/config"
"github.com/k1LoW/tbls/datasource"
"github.com/labstack/gommon/color"
"github.com/spf13/cobra"
)

// lintCmd represents the lint command
var lintCmd = &cobra.Command{
Use: "lint [DSN] [DOC_PATH]",
Short: "check database document",
Long: `'tbls lint' check database document.`,
Run: func(cmd *cobra.Command, args []string) {
c, err := config.NewConfig()
if err != nil {
printError(err)
os.Exit(1)
}

err = c.LoadConfigFile(configPath)
if err != nil {
printError(err)
os.Exit(1)
}

c.LoadArgs(args)
if err != nil {
printError(err)
os.Exit(1)
}

s, err := datasource.Analyze(c.DSN)
if err != nil {
printError(err)
os.Exit(1)
}

err = c.MergeAdditionalData(s)
if err != nil {
printError(err)
os.Exit(1)
}

l := reflect.Indirect(reflect.ValueOf(c.Lint))
t := l.Type()

ruleWarns := []config.RuleWarn{}
for i := 0; i < t.NumField(); i++ {
var v config.Rule
r := l.Field(i)
v = r.Interface().(config.Rule)
if !v.IsEnabled() {
continue
}
ruleWarns = append(ruleWarns, v.Check(s)...)
}
if len(ruleWarns) > 0 {
for _, warn := range ruleWarns {
fmt.Println(color.Red(fmt.Sprintf("%s", warn.Message)))
}
os.Exit(1)
}
},
}

func init() {
rootCmd.AddCommand(lintCmd)
lintCmd.Flags().StringVarP(&configPath, "config", "c", "", "config file path")
}
2 changes: 1 addition & 1 deletion cmd/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ var outCmd = &cobra.Command{
os.Exit(1)
}

err = s.LoadAdditionalData(c)
err = c.MergeAdditionalData(s)
if err != nil {
printError(err)
os.Exit(1)
Expand Down
78 changes: 77 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import (
"regexp"
"strings"

"github.com/k1LoW/tbls/schema"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)

var configDefaultPath = ".tbls.yml"
const configDefaultPath = ".tbls.yml"

// Config is tbls config
type Config struct {
DSN string `yaml:"dsn"`
DocPath string `yaml:"dataPath"`
Lint Lint `yaml:"lint"`
Relations []AdditionalRelation `yaml:"relations"`
Comments []AdditionalComment `yaml:"comments"`
}
Expand Down Expand Up @@ -107,6 +109,80 @@ func (c *Config) LoadConfigFile(path string) error {
return nil
}

// MergeAdditionalData merge additional* to schema.Schema
func (c *Config) MergeAdditionalData(s *schema.Schema) error {
err := mergeAdditionalRelations(s, c.Relations)
if err != nil {
return err
}
err = mergeAdditionalComments(s, c.Comments)
if err != nil {
return err
}
return nil
}

func mergeAdditionalRelations(s *schema.Schema, relations []AdditionalRelation) error {
for _, r := range relations {
relation := &schema.Relation{
IsAdditional: true,
}
if r.Def != "" {
relation.Def = r.Def
} else {
relation.Def = "Additional Relation"
}
var err error
relation.Table, err = s.FindTableByName(r.Table)
if err != nil {
return errors.Wrap(err, "failed to add relation")
}
for _, c := range r.Columns {
column, err := relation.Table.FindColumnByName(c)
if err != nil {
return errors.Wrap(err, "failed to add relation")
}
relation.Columns = append(relation.Columns, column)
column.ParentRelations = append(column.ParentRelations, relation)
}
relation.ParentTable, err = s.FindTableByName(r.ParentTable)
if err != nil {
return errors.Wrap(err, "failed to add relation")
}
for _, c := range r.ParentColumns {
column, err := relation.ParentTable.FindColumnByName(c)
if err != nil {
return errors.Wrap(err, "failed to add relation")
}
relation.ParentColumns = append(relation.ParentColumns, column)
column.ChildRelations = append(column.ChildRelations, relation)
}

s.Relations = append(s.Relations, relation)
}
return nil
}

func mergeAdditionalComments(s *schema.Schema, comments []AdditionalComment) error {
for _, c := range comments {
table, err := s.FindTableByName(c.Table)
if err != nil {
return errors.Wrap(err, "failed to add table comment")
}
if c.TableComment != "" {
table.Comment = c.TableComment
}
for c, comment := range c.ColumnComments {
column, err := table.FindColumnByName(c)
if err != nil {
return errors.Wrap(err, "failed to add column comment")
}
column.Comment = comment
}
}
return nil
}

func parseWithEnviron(v string) (string, error) {
r := regexp.MustCompile(`\${\s*([^{}]+)\s*}`)
r2 := regexp.MustCompile(`{{([^\.])`)
Expand Down
66 changes: 66 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"os"
"path/filepath"
"testing"

"github.com/k1LoW/tbls/schema"
)

func TestLoadConfigFile(t *testing.T) {
Expand Down Expand Up @@ -57,3 +59,67 @@ func testdataDir() string {
dir, _ := filepath.Abs(filepath.Join(filepath.Dir(wd), "testdata"))
return dir
}

func TestMergeAditionalData(t *testing.T) {
s := schema.Schema{
Name: "testschema",
Tables: []*schema.Table{
&schema.Table{
Name: "users",
Comment: "users comment",
Columns: []*schema.Column{
&schema.Column{
Name: "id",
Type: "serial",
},
&schema.Column{
Name: "username",
Type: "text",
},
},
},
&schema.Table{
Name: "posts",
Comment: "posts comment",
Columns: []*schema.Column{
&schema.Column{
Name: "id",
Type: "serial",
},
&schema.Column{
Name: "user_id",
Type: "int",
},
&schema.Column{
Name: "title",
Type: "text",
},
},
},
},
}
c, err := NewConfig()
if err != nil {
t.Error(err)
}
err = c.LoadConfigFile(filepath.Join(testdataDir(), "schema_test_additional_data.yml"))
if err != nil {
t.Error(err)
}
err = c.MergeAdditionalData(&s)
if err != nil {
t.Error(err)
}
expected := 1
actual := len(s.Relations)
if actual != expected {
t.Errorf("actual %v\nwant %v", actual, expected)
}
posts, _ := s.FindTableByName("posts")
title, _ := posts.FindColumnByName("title")
expected2 := "post title"
actual2 := title.Comment
if actual2 != expected2 {
t.Errorf("actual %v\nwant %v", actual2, expected2)
}
}
Loading

0 comments on commit 33ea1f6

Please sign in to comment.