google-apis-generator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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