goa_model_gen 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0cacd5ba7fc52902ba68ae788e6e4ec5a50fb6dd663da221d995c81b425f560
4
- data.tar.gz: 71368a7fdecfbdac2cc28168df06f583891833f8067c662323576ff58060fd87
3
+ metadata.gz: 82c7930eb91dbfa049e61b483ab4e570e5319020a1a0e283bf79c9014b7059f3
4
+ data.tar.gz: b7dcdd494bbfaf4f93333398c285f94b31e47d24b2c5adb643cfadb844b2c29d
5
5
  SHA512:
6
- metadata.gz: 70e0595b798469b555cf54c3fd277c5768a4ca3972a1654ec345c3d95d68cee0657fea988e43e27a8aed168ca7e360fb840a8ca8a3ca2f14aa23d81ffd74b649
7
- data.tar.gz: 9d3390c47308bada677ce7ed720fa39984a86013a49d0c0c010cec95c7e77c41ace145137d988f80206d32a814e0f433bce9ac971b918135e05893c93a3985bd
6
+ metadata.gz: 873ab6ec5df1fd224c8c07d47ca1b4d77220f9aa16cb67913fede6726f4097bd8dc2682afbff38bc6dfbb8a8ce1b5fc8a637066b67233dc9850ff3f7fc246f4e
7
+ data.tar.gz: 682549fb13dc19eb00d4c7a82be17b8ede6a6e1e8cf7c081ccff9dd52883923b2fe4e7cd6524c0c72a77b7e9ae7b3096b901387805bbcbaf2e413212733cee55
@@ -10,6 +10,8 @@ require "goa_model_gen/generator"
10
10
  module GoaModelGen
11
11
  class Cli < Thor
12
12
  class_option :version, type: :boolean, aliases: 'v', desc: 'Show version before processing'
13
+ class_option :dryrun, type: :boolean, aliases: 'd', desc: "Don't write or overwrite file"
14
+ class_option :force, type: :boolean, aliases: 'f', desc: 'Force overwrite files'
13
15
  class_option :log_level, type: :string, aliases: 'l', desc: 'Log level, one of debug,info,warn,error,fatal. The default value is info'
14
16
  class_option :config, type: :string, aliases: 'c', default: './goa_model_gen.yaml', desc: 'Path to config file. You can generate it by config subcommand'
15
17
 
@@ -19,18 +21,6 @@ module GoaModelGen
19
21
  open(path, 'w'){|f| f.puts(Config.new.fulfill.to_yaml) }
20
22
  end
21
23
 
22
- desc "bootstrap", "Generate files not concerned with model"
23
- def bootstrap
24
- setup
25
- generator = new_generator
26
- {
27
- "templates/goon.go.erb" => "model/goon.go",
28
- "templates/converter_base.go.erb" => "controller/converter_base.go",
29
- }.each do |template, dest|
30
- generator.run(template, dest, overwrite: true)
31
- end
32
- end
33
-
34
24
  desc "show FILE1...", "Show model info from definition files"
35
25
  def show(*paths)
36
26
  setup
@@ -43,27 +33,30 @@ module GoaModelGen
43
33
  desc "model FILE1...", "Generate model files from definition files"
44
34
  def model(*paths)
45
35
  setup
36
+ new_generator.process({
37
+ "templates/goon.go.erb" => File.join(cfg.model_dir, "goon.go"),
38
+ 'templates/validator.go.erb' => File.join(cfg.model_dir, 'validator.go'),
39
+ })
46
40
  load_types_for(paths) do |source_file|
47
- generator = new_generator.tap{|g| g.source_file = source_file }
48
- [
49
- {path: 'templates/model.go.erb', suffix: '.go', overwrite: true},
50
- {path: 'templates/model_validation.go.erb', suffix: '_validation.go', overwrite: false},
51
- ].each do |d|
52
- dest = File.join(cfg.model_dir, File.basename(source_file.yaml_path, ".*") + d[:suffix])
53
- generator.run(d[:path], dest, overwrite: d[:overwrite])
54
- end
41
+ new_generator.tap{|g| g.source_file = source_file }.process({
42
+ 'templates/model.go.erb' => dest_path(cfg.model_dir, source_file, '.go'),
43
+ 'templates/model_store.go.erb' => dest_path(cfg.model_dir, source_file, '_store.go'),
44
+ 'templates/model_validation.go.erb' => dest_path(cfg.model_dir, source_file, '_validation.go'),
45
+ })
55
46
  end
56
47
  end
57
48
 
58
49
  desc "converter FILE1...", "Generate converter files from definition files and swagger.yaml"
59
50
  def converter(*paths)
60
51
  setup
52
+ new_generator.process({
53
+ "templates/converter_base.go.erb" => File.join(cfg.controller_dir, "converter_base.go"),
54
+ })
61
55
  load_types_for(paths) do |source_file|
62
- generator = new_generator.tap{|g| g.source_file = source_file }
63
- dest = File.join(cfg.controller_dir, File.basename(source_file.yaml_path, ".*") + "_conv.go")
64
- if source_file.types.any?{|t| !!t.payload || !!t.media_type}
65
- generator.run('templates/converter.go.erb', dest, overwrite: true)
66
- end
56
+ next if source_file.types.all?{|t| !t.payload && !t.media_type}
57
+ new_generator.tap{|g| g.source_file = source_file }.process({
58
+ 'templates/converter.go.erb' => dest_path(cfg.controller_dir, source_file, "_conv.go"),
59
+ })
67
60
  end
68
61
  end
69
62
 
@@ -87,7 +80,10 @@ module GoaModelGen
87
80
  end
88
81
 
89
82
  def new_generator
90
- GoaModelGen::Generator.new(cfg)
83
+ GoaModelGen::Generator.new(cfg).tap do |g|
84
+ g.force = options[:force]
85
+ g.dryrun = options[:dryrun]
86
+ end
91
87
  end
92
88
 
93
89
  def load_types_for(paths)
@@ -97,6 +93,10 @@ module GoaModelGen
97
93
  yield(source_file)
98
94
  end
99
95
  end
96
+
97
+ def dest_path(dir, source_file, suffix)
98
+ File.join(dir, File.basename(source_file.yaml_path, ".*") + suffix)
99
+ end
100
100
  end
101
101
 
102
102
  end
@@ -15,6 +15,7 @@ module GoaModelGen
15
15
  gofmt_disabled
16
16
  model_dir
17
17
  controller_dir
18
+ validator_path
18
19
  ].freeze
19
20
 
20
21
  attr_accessor *ATTRIBUTES
@@ -25,6 +26,7 @@ module GoaModelGen
25
26
  @gofmt_disabled ||= false
26
27
  @model_dir ||= "./model"
27
28
  @controller_dir ||= "./controller"
29
+ @validator_path ||= "gopkg.in/go-playground/validator.v9"
28
30
  self
29
31
  end
30
32
 
@@ -9,6 +9,7 @@ module GoaModelGen
9
9
  attr_reader :name, :type, :default
10
10
  attr_accessor :format # for swagger. See https://swagger.io/docs/specification/data-models/data-types/
11
11
  attr_accessor :required
12
+ attr_accessor :unique
12
13
  attr_accessor :validation
13
14
  attr_accessor :swagger_name
14
15
  attr_reader :type_obj
@@ -19,6 +20,7 @@ module GoaModelGen
19
20
  @type = attrs['type']
20
21
  @format = attrs['format']
21
22
  @required = attrs['required']
23
+ @unique = attrs['unique']
22
24
  @default = attrs['default']
23
25
  @validation = attrs['validation']
24
26
  @goa_name = attrs['goa_name']
@@ -49,6 +51,10 @@ module GoaModelGen
49
51
  !required
50
52
  end
51
53
 
54
+ def unique?
55
+ !!unique
56
+ end
57
+
52
58
  def not_null?
53
59
  required || !default.nil?
54
60
  end
@@ -11,9 +11,13 @@ module GoaModelGen
11
11
  # These are used in templates
12
12
  attr_reader :config
13
13
  attr_accessor :source_file
14
+ attr_accessor :force, :dryrun
14
15
 
15
16
  def initialize(config)
16
17
  @config = config
18
+ @user_editable = false
19
+ @force = false
20
+ @dryrun = false
17
21
  end
18
22
 
19
23
  def golang_helper
@@ -42,6 +46,13 @@ module GoaModelGen
42
46
  end
43
47
  end
44
48
 
49
+ def user_editable(value: true)
50
+ @user_editable = value
51
+ end
52
+ def user_editable?
53
+ @user_editable
54
+ end
55
+
45
56
  GO_BASE_PATH = File.expand_path('../templates/base.go.erb', __FILE__)
46
57
 
47
58
  PACKAGE_FOR_IMPORT = {
@@ -63,13 +74,36 @@ module GoaModelGen
63
74
  base.result(binding).strip
64
75
  end
65
76
 
66
- def run(template_path, output_path, overwrite: false)
67
- return if File.exist?(output_path) && !overwrite
77
+ COLORS = {
78
+ generate: "\e[32m", # green # !file_exist
79
+ no_change: "\e[37m", # white # file_exist && !modified
80
+ overwrite: "\e[33m", # yellow # file_exist && !user_editable
81
+ keep: "\e[34m", # blue # file_exist && user_editable && !force
82
+ force_overwrite: "\e[31m", # red # file_exist && user_editable && force
83
+ clear: "\e[0m", # clear
84
+ }
85
+ MAX_ACTION_LENGTH = COLORS.keys.map(&:to_s).map(&:length).max
86
+
87
+ def run(template_path, output_path)
88
+ already_exist = File.exist?(output_path)
68
89
  content = generate(template_path)
90
+ modified = already_exist ? (content != File.read(output_path)) : true
91
+ action =
92
+ !already_exist ? :generate :
93
+ !modified ? :no_change :
94
+ !user_editable? ? :overwrite :
95
+ force ? :force_overwrite : :keep
96
+ GoaModelGen.logger.info("%s%-#{MAX_ACTION_LENGTH}s %s%s" % [COLORS[action], action.to_s, output_path, COLORS[:clear]])
97
+ return if action == :no_change
98
+ return if dryrun
69
99
  open(output_path, 'w'){|f| f.puts(content) }
70
100
  if (File.extname(output_path) == '.go') && !config.gofmt_disabled
71
101
  system("gofmt -w #{output_path}")
72
102
  end
73
103
  end
104
+
105
+ def process(temp_path_to_dest_path)
106
+ temp_path_to_dest_path.each{|src, dest| run(src, dest) }
107
+ end
74
108
  end
75
109
  end
@@ -1,3 +1,10 @@
1
+ <%- if user_editable? -%>
2
+ // You can edit this file. goa_model_gen doesn't overwrite this file.
3
+ <%- else -%>
4
+ // DO NOT EDIT this file.
5
+ <%- end -%>
6
+ // This code generated by goa_model_gen-<%= GoaModelGen::VERSION %>
7
+
1
8
  package <%= package %>
2
9
 
3
10
  <%- unless dependencies.empty? -%>
@@ -1,3 +1,4 @@
1
+ <%- user_editable -%>
1
2
  <%- package 'model' -%>
2
3
 
3
4
  <%-
@@ -1,14 +1,14 @@
1
1
  <%- package "model" -%>
2
2
 
3
3
  <%- source_file.types.select(&:store?).each do |model| -%>
4
- <%- store_name = "#{model.name}Store" -%>
4
+ <%- store_name = "#{model.name}Store" -%>
5
5
  <%-
6
- import(
7
- "context",
8
- "fmt",
9
- "google.golang.org/appengine/datastore",
10
- "google.golang.org/appengine/log",
11
- )
6
+ import(
7
+ "context",
8
+ "fmt",
9
+ "google.golang.org/appengine/datastore",
10
+ "google.golang.org/appengine/log",
11
+ )
12
12
  -%>
13
13
  type <%= store_name %> struct{
14
14
  <%- if model.parent -%>
@@ -32,6 +32,16 @@ func (s *<%= store_name %>) Select(ctx context.Context, q *datastore.Query) ([]*
32
32
  return r, nil
33
33
  }
34
34
 
35
+ func (s *<%= store_name %>) CountBy(ctx context.Context, q *datastore.Query) (int, error) {
36
+ g := GoonFromContext(ctx)
37
+ c, err := g.Count(q)
38
+ if err != nil {
39
+ log.Errorf(ctx, "Failed to count <%= model.name %> with %v because of %v\n", q, err)
40
+ return 0, err
41
+ }
42
+ return c, nil
43
+ }
44
+
35
45
  func (s *<%= store_name %>) Query(ctx context.Context) *datastore.Query {
36
46
  g := GoonFromContext(ctx)
37
47
  k := g.Kind(new(<%= model.name %>))
@@ -45,7 +55,7 @@ func (s *<%= store_name %>) ByID(ctx context.Context, <%= model.id_name_var %> <
45
55
  <%- else -%>
46
56
  r := <%= model.name %>{<%= model.id_name %>: <%= model.id_name_var %>}
47
57
  <%- end -%>
48
- err := s.Get(ctx, &r)
58
+ err := s.Get(ctx, &r)
49
59
  if err != nil {
50
60
  return nil, err
51
61
  }
@@ -123,54 +133,57 @@ func (s *<%= store_name %>) Exist(ctx context.Context, m *<%= model.name %>) (bo
123
133
  }
124
134
 
125
135
  func (s *<%= store_name %>) Create(ctx context.Context, m *<%= model.name %>) (*datastore.Key, error) {
126
- err := m.PrepareToCreate()
127
- if err != nil {
128
- return nil, err
129
- }
130
- if err := m.Validate(); err != nil {
131
- return nil, err
132
- }
133
-
134
- <%- if model.goon['id_type'] == 'string' -%>
135
- exist, err := s.Exist(ctx, m)
136
+ err := m.PrepareToCreate()
136
137
  if err != nil {
137
138
  return nil, err
138
139
  }
139
- if exist {
140
- log.Errorf(ctx, "Failed to create %v because of another entity has same key\n", m)
141
- return nil, fmt.Errorf("Duplicate <%= model.goon['id_name'] %> error: %q of %v\n", m.<%= model.goon['id_name'] %>, m)
142
- }
143
- <%- end -%>
144
-
145
- return s.Put(ctx, m)
140
+ return s.PutWith(ctx, m, func() error {
141
+ exist, err := s.Exist(ctx, m)
142
+ if err != nil {
143
+ return err
144
+ }
145
+ if exist {
146
+ log.Errorf(ctx, "Failed to create %v because of another entity has same key\n", m)
147
+ return fmt.Errorf("Duplicate <%= model.goon['id_name'] %> error: %q of %v\n", m.<%= model.goon['id_name'] %>, m)
148
+ }
149
+ return nil
150
+ })
146
151
  }
147
152
 
148
153
  func (s *<%= store_name %>) Update(ctx context.Context, m *<%= model.name %>) (*datastore.Key, error) {
149
- err := m.PrepareToUpdate()
150
- if err != nil {
151
- return nil, err
152
- }
153
- if err := m.Validate(); err != nil {
154
+ err := m.PrepareToUpdate()
155
+ if err != nil {
154
156
  return nil, err
155
157
  }
158
+ return s.PutWith(ctx, m, func() error {
159
+ exist, err := s.Exist(ctx, m)
160
+ if err != nil {
161
+ return err
162
+ }
163
+ if !exist {
164
+ log.Errorf(ctx, "Failed to update %v because it doesn't exist\n", m)
165
+ return fmt.Errorf("No data to update %q of %v\n", m.<%= model.goon['id_name'] %>, m)
166
+ }
167
+ return nil
168
+ })
169
+ }
156
170
 
157
- <%- if model.goon['id_type'] == 'string' -%>
158
- exist, err := s.Exist(ctx, m)
159
- if err != nil {
171
+ func (s *<%= store_name %>) PutWith(ctx context.Context, m *<%= model.name %>, f func() error) (*datastore.Key, error) {
172
+ if err := s.Validate(ctx, m); err != nil {
160
173
  return nil, err
161
174
  }
162
- if !exist {
163
- log.Errorf(ctx, "Failed to update %v because it doesn't exist\n", m)
164
- return nil, fmt.Errorf("No data to update %q of %v\n", m.<%= model.goon['id_name'] %>, m)
175
+ if f != nil {
176
+ if err := f(); err != nil {
177
+ return nil, err
178
+ }
165
179
  }
166
- <%- end -%>
167
180
 
168
- return s.Put(ctx, m)
181
+ return s.Put(ctx, m)
169
182
  }
170
183
 
171
184
  func (s *<%= store_name %>) Put(ctx context.Context, m *<%= model.name %>) (*datastore.Key, error) {
172
185
  <%- if model.goon['id_type'] == 'UUID' -%>
173
- <%- import "github.com/goadesign/goa/uuid" -%>
186
+ <%- import "github.com/goadesign/goa/uuid" -%>
174
187
  if m.Id == "" {
175
188
  m.Id = uuid.NewV4().String()
176
189
  }
@@ -193,13 +206,13 @@ func (s *<%= store_name %>) Put(ctx context.Context, m *<%= model.name %>) (*dat
193
206
  <%- if model.parent -%>
194
207
  func (s *<%= store_name %>) ValidateParent(m *<%= model.name %>) error {
195
208
  if s.ParentKey == nil {
196
- return nil
209
+ return nil
197
210
  }
198
211
  if m.ParentKey == nil {
199
212
  m.ParentKey = s.ParentKey
200
213
  }
201
214
  if !s.ParentKey.Equal(m.ParentKey) {
202
- return fmt.Errorf("Invalid ParentKey for %v", m)
215
+ return fmt.Errorf("Invalid ParentKey for %v", m)
203
216
  }
204
217
  return nil
205
218
  }
@@ -219,4 +232,26 @@ func (s *<%= store_name %>) Delete(ctx context.Context, m *<%= model.name %>) er
219
232
  return nil
220
233
  }
221
234
 
235
+ func (s *<%= store_name %>) ValidateUniqueness(ctx context.Context, m *<%= model.name %>) error {
236
+ conditions := map[string]interface{}{
237
+ <%- model.fields.select(&:unique?).each do |field| -%>
238
+ "<%= field.name %>": m.<%= field.name %>,
239
+ <%- end -%>
240
+ }
241
+ for field, value := range conditions {
242
+ q := s.Query(ctx).Filter(field + " =", value)
243
+ c, err := s.CountBy(ctx, q)
244
+ if err != nil {
245
+ return err
246
+ }
247
+ if c > 0 {
248
+ return &ValidationError{
249
+ Field: field,
250
+ Message: fmt.Sprintf("%v has already been taken", value),
251
+ }
252
+ }
253
+ }
254
+ return nil
255
+ }
256
+
222
257
  <%- end -%>
@@ -1,10 +1,26 @@
1
+ <%- user_editable -%>
1
2
  <%- package "model" -%>
2
3
 
3
4
  <%- source_file.types.select(&:store?).each do |model| -%>
4
- <%- import "gopkg.in/go-playground/validator.v9" -%>
5
- func (m *<%= model.name %>) Validate() error {
6
- validator := validator.New()
7
- return validator.Struct(m)
5
+ <%-
6
+ import "context"
7
+ import config.validator_path # "gopkg.in/go-playground/validator.v9"
8
+ -%>
9
+ <%- store_name = "#{model.name}Store" -%>
10
+ func (s *<%= store_name %>) Validate(ctx context.Context, m *<%= model.name %>) error {
11
+ if err := m.Validate(ctx); err != nil {
12
+ return err
13
+ }
14
+ if err := s.ValidateUniqueness(ctx, m); err != nil {
15
+ return err
16
+ }
17
+ return nil
18
+ }
19
+
20
+ func (m *<%= model.name %>) Validate(ctx context.Context) error {
21
+ return WithValidator(ctx, func(validate *validator.Validate) error {
22
+ return validate.Struct(m)
23
+ })
8
24
  }
9
25
 
10
26
  <%- end -%>
@@ -0,0 +1,28 @@
1
+ <%- user_editable -%>
2
+ <%- package "model" -%>
3
+ <%-
4
+ import(
5
+ "context",
6
+ config.validator_path,
7
+ # ja_translations "github.com/akm/validator/translations/ja",
8
+ )
9
+ -%>
10
+
11
+ func NewValidator(ctx context.Context) (*validator.Validate, error) {
12
+ validate := validator.New()
13
+ // trans, err := GetTranslator(ctx)
14
+ // if err != nil {
15
+ // return nil, err
16
+ // }
17
+ // // See https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go
18
+ // ja_translations.RegisterDefaultTranslations(validate, trans)
19
+ return validate, nil
20
+ }
21
+
22
+ func WithValidator(ctx context.Context, f func(*validator.Validate) error) error {
23
+ validate, err := NewValidator(ctx)
24
+ if err != nil {
25
+ return err
26
+ }
27
+ return f(validate)
28
+ }
@@ -1,3 +1,3 @@
1
1
  module GoaModelGen
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goa_model_gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - akm
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-05 00:00:00.000000000 Z
11
+ date: 2018-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -131,6 +131,7 @@ files:
131
131
  - lib/goa_model_gen/templates/model.go.erb
132
132
  - lib/goa_model_gen/templates/model_store.go.erb
133
133
  - lib/goa_model_gen/templates/model_validation.go.erb
134
+ - lib/goa_model_gen/templates/validator.go.erb
134
135
  - lib/goa_model_gen/type.rb
135
136
  - lib/goa_model_gen/version.rb
136
137
  homepage: https://github.com/akm/goa_model_gen
@@ -153,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  version: '0'
154
155
  requirements: []
155
156
  rubyforge_project:
156
- rubygems_version: 2.7.6
157
+ rubygems_version: 2.7.3
157
158
  signing_key:
158
159
  specification_version: 4
159
160
  summary: Generate model files for goa in golang