google-apis-generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ # Copyright 2015 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'active_support/inflector'
16
+ require 'google/apis/discovery_v1'
17
+
18
+ # Extend the discovery API classes with additional data needed to make
19
+ # code generation produce better results
20
+ module Google
21
+ module Apis
22
+ module DiscoveryV1
23
+ TYPE_MAP = {
24
+ 'string' => 'String',
25
+ 'boolean' => 'Boolean',
26
+ 'number' => 'Float',
27
+ 'integer' => 'Fixnum',
28
+ 'any' => 'Object'
29
+ }
30
+
31
+ class JsonSchema
32
+ attr_accessor :name
33
+ attr_accessor :generated_name
34
+ attr_accessor :generated_class_name
35
+ attr_accessor :base_ref
36
+ attr_accessor :parent
37
+ attr_accessor :discriminant
38
+ attr_accessor :discriminant_value
39
+ attr_accessor :path
40
+
41
+ def properties
42
+ Hash[(@properties || {}).sort]
43
+ end
44
+
45
+ def qualified_name
46
+ parent.qualified_name + '::' + generated_class_name
47
+ end
48
+
49
+ def generated_type
50
+ case type
51
+ when 'string', 'boolean', 'number', 'integer', 'any'
52
+ return 'DateTime' if format == 'date-time'
53
+ return 'Date' if format == 'date'
54
+ return 'Fixnum' if format == 'int64'
55
+ return 'Fixnum' if format == 'uint64'
56
+ return TYPE_MAP[type]
57
+ when 'array'
58
+ if items == self
59
+ return sprintf('Array<%s>', qualified_name)
60
+ end
61
+ return sprintf('Array<%s>', items.generated_type)
62
+ when 'hash'
63
+ if additional_properties == self
64
+ return sprintf('Hash<String,%s>', qualified_name)
65
+ end
66
+ return sprintf('Hash<String,%s>', additional_properties.generated_type)
67
+ when 'object'
68
+ return qualified_name
69
+ end
70
+ end
71
+ end
72
+
73
+ class RestMethod
74
+ attr_accessor :generated_name
75
+ attr_accessor :parent
76
+
77
+ def parameters
78
+ Hash[(@parameters || {}).sort]
79
+ end
80
+
81
+ def path_parameters
82
+ return [] if parameter_order.nil? || parameters.nil?
83
+ parameter_order.map { |name| parameters[name] }.select { |param| param.location == 'path' }
84
+ end
85
+
86
+ def query_parameters
87
+ return [] if parameters.nil?
88
+ parameters.values.select { |param| param.location == 'query' }
89
+ end
90
+
91
+ def required_parameters
92
+ return [] if parameter_order.nil? || parameters.nil?
93
+ parameter_order.map { |name| parameters[name] }.select { |param| param.location == 'path' || param.required }
94
+ end
95
+
96
+ def optional_query_parameters
97
+ query_parameters.select { |param| param.required != true }
98
+ end
99
+
100
+ end
101
+
102
+ class RestResource
103
+ attr_accessor :parent
104
+
105
+ def api_methods
106
+ Hash[(@api_methods || {}).sort]
107
+ end
108
+
109
+ def resources
110
+ Hash[(@resources || {}).sort]
111
+ end
112
+
113
+ def all_methods
114
+ m = []
115
+ m << api_methods.values unless api_methods.nil?
116
+ m << resources.map { |_k, r| r.all_methods } unless resources.nil?
117
+ m.flatten
118
+ end
119
+ end
120
+
121
+ class RestDescription
122
+ attr_accessor :force_alt_json
123
+ alias_method :force_alt_json?, :force_alt_json
124
+
125
+ # Don't expose these in the API directly.
126
+ PARAMETER_BLACKLIST = %w(alt access_token bearer_token oauth_token pp prettyPrint
127
+ $.xgafv callback upload_protocol uploadType)
128
+
129
+ def version
130
+ ActiveSupport::Inflector.camelize(@version.gsub(/\W/, '-')).gsub(/-/, '_')
131
+ end
132
+
133
+ def name
134
+ ActiveSupport::Inflector.camelize(@name)
135
+ end
136
+
137
+ def module_name
138
+ name + version
139
+ end
140
+
141
+ def qualified_name
142
+ sprintf('Google::Apis::%s', module_name)
143
+ end
144
+
145
+ def base_path
146
+ ActiveSupport::Inflector.underscore(qualified_name)
147
+ end
148
+
149
+ def gem_name
150
+ base_path.tr("/", "-")
151
+ end
152
+
153
+ def service_name
154
+ class_name = (canonical_name || name).gsub(/\W/, '')
155
+ ActiveSupport::Inflector.camelize(sprintf('%sService', class_name))
156
+ end
157
+
158
+ def api_methods
159
+ Hash[(@api_methods || {}).sort]
160
+ end
161
+
162
+ def resources
163
+ Hash[(@resources || {}).sort]
164
+ end
165
+
166
+ def all_methods
167
+ m = []
168
+ m << api_methods.values unless api_methods.nil?
169
+ m << resources.map { |_k, r| r.all_methods } unless resources.nil?
170
+ m.flatten
171
+ end
172
+
173
+ def parameters
174
+ Hash[(@parameters || {}).sort].delete_if { |k, _v| PARAMETER_BLACKLIST.include?(k) }
175
+ end
176
+
177
+ def schemas
178
+ Hash[(@schemas || {}).sort]
179
+ end
180
+
181
+ class Auth
182
+ class Oauth2
183
+ class Scope
184
+ attr_accessor :constant
185
+ end
186
+
187
+ def scopes
188
+ Hash[(@scopes || {}).sort]
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright 2015 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'active_support/inflector'
16
+ require 'erb'
17
+ require 'ostruct'
18
+
19
+ module Google
20
+ module Apis
21
+ # @private
22
+ class Generator
23
+ # Directory containing ERB templates
24
+ TEMPLATE_DIR = File.expand_path('../templates', __FILE__)
25
+
26
+ # Helpers used in ERB templates
27
+ module TemplateHelpers
28
+ # Get the include path for a ruby module/class
29
+ #
30
+ # @param [String] module_name
31
+ # Fully qualified module/class name
32
+ # @return [String]
33
+ # Path to file
34
+ def to_path(module_name)
35
+ ActiveSupport::Inflector.underscore(module_name)
36
+ end
37
+
38
+ # Render a block comment
39
+ #
40
+ # @param [String] str
41
+ # Comment string
42
+ # @param [Fixnum] spaces_before
43
+ # Number of spaces to indent the comment hash
44
+ # @param [Fixnum] spaces_after
45
+ # Number of spaces to indent after the comment hash for subsequent lines
46
+ # @return [String] formatted comment
47
+ def block_comment(str, spaces_before = 0, spaces_after = 0)
48
+ return '' if str.nil?
49
+ pre = ' ' * spaces_before
50
+ post = ' ' * spaces_after
51
+ lines = str.gsub(/([{}])/, '`').scan(/.{1,78}(?:\W|$)/).map(&:strip)
52
+ lines.join("\n" + pre + '#' + post)
53
+ end
54
+
55
+ # Indent a block of text
56
+ #
57
+ # @param [String] str
58
+ # Content to indent
59
+ # @param [Fixnum] spaces
60
+ # Number of spaces to indent
61
+ # @return [String] formatted content
62
+ def indent(str, spaces)
63
+ pre = ' ' * spaces
64
+ str = pre + str.split(/\n/).join("\n" + pre) + "\n"
65
+ return str unless str.strip.empty?
66
+ nil
67
+ end
68
+
69
+ # Include a partial inside a template.
70
+ #
71
+ # @private
72
+ # @param [String] partial
73
+ # Name of the template
74
+ # @param [Hash] context
75
+ # Context used to render
76
+ # @return [String] rendered content
77
+ def include(partial, context)
78
+ template = Template.new(sprintf('_%s.tmpl', partial))
79
+ template.render(context)
80
+ end
81
+ end
82
+
83
+ # Holds local vars/helpers for template rendering
84
+ class Context < OpenStruct
85
+ include TemplateHelpers
86
+
87
+ # Get the context for ERB evaluation
88
+ # @return [Binding]
89
+ def to_binding
90
+ binding
91
+ end
92
+ end
93
+
94
+ # ERB template for the code generator
95
+ class Template
96
+ # Loads a template from the template dir. Automatically
97
+ # appends the .tmpl suffix
98
+ #
99
+ # @param [String] template_name
100
+ # Name of the template file
101
+ def self.load(template_name)
102
+ Template.new(sprintf('%s.tmpl', template_name))
103
+ end
104
+
105
+ # @param [String] template_name
106
+ # Name of the template file
107
+ def initialize(template_name)
108
+ file = File.join(TEMPLATE_DIR, template_name)
109
+ @erb = ERB.new(File.read(file), nil, '-')
110
+ end
111
+
112
+ # Render the template
113
+ #
114
+ # @param [Hash] context
115
+ # Variables to set when rendering the template
116
+ # @return [String] rendered template
117
+ def render(context)
118
+ ctx = Context.new(context)
119
+ @erb.result(ctx.to_binding)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,139 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "gems"
16
+
17
+ module Google
18
+ module Apis
19
+ class Generator
20
+ # Matches generated files against current files and decides what to update
21
+ # @private
22
+ class Updater
23
+ def initialize
24
+ @gems_client = nil
25
+ @current_rubygems_versions = {}
26
+ end
27
+
28
+ def analyze(base_dir, generator_result)
29
+ modified_files = {}
30
+ version_content = changelog_content = nil
31
+ generator_result.files.each do |path, content|
32
+ full_path = File.join(base_dir, path)
33
+ old_content = File.binread(full_path) if File.readable?(full_path)
34
+ if path == generator_result.version_path
35
+ version_content = old_content || content
36
+ elsif path == generator_result.changelog_path
37
+ changelog_content = old_content || content
38
+ else
39
+ modified_files[path] = content unless content == old_content
40
+ end
41
+ end
42
+ unless modified_files.empty?
43
+ desired_gem_version = next_rubygems_version(generator_result.gem_name)
44
+ version_content, generator_version_change, revision_change =
45
+ update_version_content(version_content, desired_gem_version, generator_result.revision)
46
+ changelog_content = update_changelog_content(changelog_content, desired_gem_version, generator_version_change, revision_change)
47
+ modified_files[generator_result.version_path] = version_content
48
+ modified_files[generator_result.changelog_path] = changelog_content
49
+ end
50
+ modified_files
51
+ end
52
+
53
+ # @private
54
+ def gems_client
55
+ @gems_client ||= Gems::Client.new
56
+ end
57
+
58
+ # @private
59
+ def current_rubygems_version(gem_name)
60
+ @current_rubygems_versions[gem_name] ||= begin
61
+ gems_client.info(gem_name)["version"]
62
+ rescue Gems::NotFound
63
+ "0.0.0"
64
+ end
65
+ end
66
+
67
+ # @private
68
+ def next_rubygems_version(gem_name)
69
+ major, minor = current_rubygems_version(gem_name).split(".")
70
+ "#{major.to_i}.#{minor.to_i + 1}.0"
71
+ end
72
+
73
+ # @private
74
+ def update_version_content(content, desired_gem_version, new_revision)
75
+ generator_version_change = revision_change = nil
76
+ modified_content = content.dup
77
+ modified_content.sub!(/GEM_VERSION = "([\w\.]*)"/) do |*|
78
+ "GEM_VERSION = \"#{desired_gem_version}\""
79
+ end or raise "gem_version.rb is missing GEM_VERSION"
80
+ modified_content.sub!(/GENERATOR_VERSION = "([\w\.]*)"/) do |*|
81
+ generator_version_change = Generator::VERSION unless Regexp.last_match[1] == Generator::VERSION
82
+ "GENERATOR_VERSION = \"#{Generator::VERSION}\""
83
+ end or raise "gem_version.rb is missing GENERATOR_VERSION"
84
+ modified_content.sub!(/REVISION = "([\w\.]*)"/) do |*|
85
+ revision_change = new_revision unless Regexp.last_match[1] == new_revision
86
+ "REVISION = \"#{new_revision}\""
87
+ end or raise "gem_version.rb is missing REVISION"
88
+ [modified_content, generator_version_change, revision_change]
89
+ end
90
+
91
+ # @private
92
+ def update_changelog_content(content, desired_gem_version, generator_version_change, revision_change)
93
+ lines = parse_existing_changelog_entry(content, desired_gem_version)
94
+ modify_changelog_lines(lines, generator_version_change, revision_change)
95
+ entry = assemble_changelog_entry(lines, desired_gem_version)
96
+ replace_changelog_entry(content, desired_gem_version, entry)
97
+ end
98
+
99
+ # @private
100
+ def parse_existing_changelog_entry(content, desired_gem_version)
101
+ quoted_gem_version = Regexp.quote(desired_gem_version)
102
+ match = /\n+### v#{quoted_gem_version} \([\d-]+\)\n+((?:[^#][^\n]*\n+)*)(?=#|$)/.match content
103
+ return [] unless match
104
+ match[1].split("\n")
105
+ end
106
+
107
+ # @private
108
+ def modify_changelog_lines(lines, generator_version_change, revision_change)
109
+ if generator_version_change
110
+ lines.reject! { |line| line =~ /^\* Regenerated using generator version \d[\w\.]+/ }
111
+ lines.unshift("* Regenerated using generator version #{generator_version_change}")
112
+ end
113
+ if revision_change
114
+ lines.reject! { |line| line =~ /^\* Regenerated from discovery document revision \d+/ }
115
+ lines.unshift("* Regenerated from discovery document revision #{revision_change}")
116
+ end
117
+ lines << "* Unspecified changes" if lines.empty?
118
+ end
119
+
120
+ # @private
121
+ def assemble_changelog_entry(lines, desired_gem_version)
122
+ entry_lines = lines.join("\n")
123
+ date = Time.now.strftime("%Y-%m-%d")
124
+ "\n\n### v#{desired_gem_version} (#{date})\n\n#{entry_lines}\n\n"
125
+ end
126
+
127
+ # @private
128
+ def replace_changelog_entry(content, desired_gem_version, entry)
129
+ quoted_gem_version = Regexp.quote(desired_gem_version)
130
+ modified_content = content.dup
131
+ modified_content.sub!(/\n+### v#{quoted_gem_version} \([\d-]+\)\n+(?:[^#][^\n]*\n+)*(?=#|$)/, entry) or
132
+ modified_content.sub!(/^(\n*# [^\n]+)\n+(?=#|$)/, "\\1#{entry}") or
133
+ raise "CHANGELOG doesn't seem to have the expected header"
134
+ modified_content
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end