google-api-client 0.2.0 → 0.3.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,149 @@
1
+ # Copyright 2010 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
+
16
+ require 'addressable/uri'
17
+
18
+ require 'google/inflection'
19
+ require 'google/api_client/discovery/method'
20
+
21
+ module Google
22
+ class APIClient
23
+ ##
24
+ # A resource that has been described by a discovery document.
25
+ class Resource
26
+
27
+ ##
28
+ # Creates a description of a particular version of a resource.
29
+ #
30
+ # @param [Addressable::URI] base
31
+ # The base URI for the service.
32
+ # @param [String] resource_name
33
+ # The identifier for the resource.
34
+ # @param [Hash] resource_description
35
+ # The section of the discovery document that applies to this resource.
36
+ #
37
+ # @return [Google::APIClient::Resource] The constructed resource object.
38
+ def initialize(api, method_base, resource_name, discovery_document)
39
+ @api = api
40
+ @method_base = method_base
41
+ @name = resource_name
42
+ @discovery_document = discovery_document
43
+ metaclass = (class <<self; self; end)
44
+ self.resources.each do |resource|
45
+ method_name = Google::INFLECTOR.underscore(resource.name).to_sym
46
+ if !self.respond_to?(method_name)
47
+ metaclass.send(:define_method, method_name) { resource }
48
+ end
49
+ end
50
+ self.methods.each do |method|
51
+ method_name = Google::INFLECTOR.underscore(method.name).to_sym
52
+ if !self.respond_to?(method_name)
53
+ metaclass.send(:define_method, method_name) { method }
54
+ end
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Returns the identifier for the resource.
60
+ #
61
+ # @return [String] The resource identifier.
62
+ attr_reader :name
63
+
64
+ ##
65
+ # Returns the parsed section of the discovery document that applies to
66
+ # this resource.
67
+ #
68
+ # @return [Hash] The resource description.
69
+ attr_reader :description
70
+
71
+ ##
72
+ # Returns the base URI for this resource.
73
+ #
74
+ # @return [Addressable::URI] The base URI that methods are joined to.
75
+ attr_reader :method_base
76
+
77
+ ##
78
+ # Updates the hierarchy of resources and methods with the new base.
79
+ #
80
+ # @param [Addressable::URI, #to_str, String] new_base
81
+ # The new base URI to use for the resource.
82
+ def method_base=(new_method_base)
83
+ @method_base = Addressable::URI.parse(new_method_base)
84
+ self.resources.each do |resource|
85
+ resource.method_base = @method_base
86
+ end
87
+ self.methods.each do |method|
88
+ method.method_base = @method_base
89
+ end
90
+ end
91
+
92
+ ##
93
+ # A list of sub-resources available on this resource.
94
+ #
95
+ # @return [Array] A list of {Google::APIClient::Resource} objects.
96
+ def resources
97
+ return @resources ||= (
98
+ (@discovery_document['resources'] || []).inject([]) do |accu, (k, v)|
99
+ accu << Google::APIClient::Resource.new(
100
+ @api, self.method_base, k, v
101
+ )
102
+ accu
103
+ end
104
+ )
105
+ end
106
+
107
+ ##
108
+ # A list of methods available on this resource.
109
+ #
110
+ # @return [Array] A list of {Google::APIClient::Method} objects.
111
+ def methods
112
+ return @methods ||= (
113
+ (@discovery_document['methods'] || []).inject([]) do |accu, (k, v)|
114
+ accu << Google::APIClient::Method.new(@api, self.method_base, k, v)
115
+ accu
116
+ end
117
+ )
118
+ end
119
+
120
+ ##
121
+ # Converts the resource to a flat mapping of RPC names and method
122
+ # objects.
123
+ #
124
+ # @return [Hash] All methods available on the resource.
125
+ def to_h
126
+ return @hash ||= (begin
127
+ methods_hash = {}
128
+ self.methods.each do |method|
129
+ methods_hash[method.id] = method
130
+ end
131
+ self.resources.each do |resource|
132
+ methods_hash.merge!(resource.to_h)
133
+ end
134
+ methods_hash
135
+ end)
136
+ end
137
+
138
+ ##
139
+ # Returns a <code>String</code> representation of the resource's state.
140
+ #
141
+ # @return [String] The resource's state, as a <code>String</code>.
142
+ def inspect
143
+ sprintf(
144
+ "#<%s:%#0x NAME:%s>", self.class.to_s, self.object_id, self.name
145
+ )
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,106 @@
1
+ # Copyright 2010 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
+
16
+ require 'time'
17
+ require 'json'
18
+ require 'base64'
19
+ require 'autoparse'
20
+ require 'addressable/uri'
21
+ require 'addressable/template'
22
+
23
+ require 'google/inflection'
24
+ require 'google/api_client/errors'
25
+
26
+ module Google
27
+ class APIClient
28
+ module Schema
29
+ def self.parse(api, schema_data)
30
+ # This method is super-long, but hard to break up due to the
31
+ # unavoidable dependence on closures and execution context.
32
+ schema_name = schema_data['id']
33
+
34
+ # Due to an oversight, schema IDs may not be URI references.
35
+ # TODO(bobaman): Remove this code once this has been resolved.
36
+ schema_uri = (
37
+ api.document_base +
38
+ (schema_name[0..0] != '#' ? '#' + schema_name : schema_name)
39
+ )
40
+ # puts schema_uri
41
+
42
+ # Due to an oversight, schema IDs may not be URI references.
43
+ # TODO(bobaman): Remove this whole lambda once this has been resolved.
44
+ reformat_references = lambda do |data|
45
+ # This code is not particularly efficient due to recursive traversal
46
+ # and excess object creation, but this hopefully shouldn't be an
47
+ # issue since it should only be called only once per schema per
48
+ # process.
49
+ if data.kind_of?(Hash) && data['$ref']
50
+ reference = data['$ref']
51
+ reference = '#' + reference if reference[0..0] != '#'
52
+ data.merge({
53
+ '$ref' => reference
54
+ })
55
+ elsif data.kind_of?(Hash)
56
+ data.inject({}) do |accu, (key, value)|
57
+ if value.kind_of?(Hash)
58
+ accu[key] = reformat_references.call(value)
59
+ else
60
+ accu[key] = value
61
+ end
62
+ accu
63
+ end
64
+ else
65
+ data
66
+ end
67
+ end
68
+ schema_data = reformat_references.call(schema_data)
69
+ # puts schema_data.inspect
70
+
71
+ if schema_name
72
+ api_name_string =
73
+ Google::INFLECTOR.camelize(api.name)
74
+ api_version_string =
75
+ Google::INFLECTOR.camelize(api.version).gsub('.', '_')
76
+ if Google::APIClient::Schema.const_defined?(api_name_string)
77
+ api_name = Google::APIClient::Schema.const_get(api_name_string)
78
+ else
79
+ api_name = Google::APIClient::Schema.const_set(
80
+ api_name_string, Module.new
81
+ )
82
+ end
83
+ if api_name.const_defined?(api_version_string)
84
+ api_version = api_name.const_get(api_version_string)
85
+ else
86
+ api_version = api_name.const_set(api_version_string, Module.new)
87
+ end
88
+ if api_version.const_defined?(schema_name)
89
+ schema_class = api_version.const_get(schema_name)
90
+ end
91
+ end
92
+
93
+ # It's possible the schema has already been defined. If so, don't
94
+ # redefine it. This means that reloading a schema which has already
95
+ # been loaded into memory is not possible.
96
+ unless schema_class
97
+ schema_class = AutoParse.generate(schema_data, :uri => schema_uri)
98
+ if schema_name
99
+ api_version.const_set(schema_name, schema_class)
100
+ end
101
+ end
102
+ return schema_class
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,8 +1,11 @@
1
1
  module Google
2
2
  class APIClient
3
3
  module ENV
4
- OS_VERSION = if RUBY_PLATFORM =~ /win32/
5
- `ver`.sub(/\s*\[Version\s*/, '/').sub(']', '')
4
+ OS_VERSION = if RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/
5
+ # TODO(bobaman)
6
+ # Confirm that all of these Windows environments actually have access
7
+ # to the `ver` command.
8
+ `ver`.sub(/\s*\[Version\s*/, '/').sub(']', '').strip
6
9
  elsif RUBY_PLATFORM =~ /darwin/i
7
10
  "Mac OS X/#{`sw_vers -productVersion`}"
8
11
  else
@@ -26,5 +26,15 @@ module Google
26
26
  # invalid parameter values.
27
27
  class ValidationError < StandardError
28
28
  end
29
+
30
+ ##
31
+ # A 4xx class HTTP error occurred.
32
+ class ClientError < TransmissionError
33
+ end
34
+
35
+ ##
36
+ # A 5xx class HTTP error occurred.
37
+ class ServerError < TransmissionError
38
+ end
29
39
  end
30
40
  end
@@ -0,0 +1,59 @@
1
+ # Copyright 2010 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
+
16
+ require 'json'
17
+
18
+ module Google
19
+ class APIClient
20
+ module Parser
21
+ def content_type(content_type)
22
+ @@content_type_mapping ||= {}
23
+ @@content_type_mapping[content_type] = self
24
+ end
25
+
26
+ def self.match_content_type(content_type)
27
+ # TODO(bobaman): Do this more efficiently.
28
+ mime_type_regexp = /^([^\/]+)(?:\/([^+]+\+)?([^;]+))?(?:;.*)?$/
29
+ if @@content_type_mapping[content_type]
30
+ # Exact match
31
+ return @@content_type_mapping[content_type]
32
+ else
33
+ media_type, extension, sub_type =
34
+ content_type.scan(mime_type_regexp)[0]
35
+ for pattern, parser in @@content_type_mapping
36
+ # We want to match on subtype first
37
+ pattern_media_type, pattern_extension, pattern_sub_type =
38
+ pattern.scan(mime_type_regexp)[0]
39
+ next if pattern_extension != nil
40
+ if media_type == pattern_media_type && sub_type == pattern_sub_type
41
+ return parser
42
+ end
43
+ end
44
+ for pattern, parser in @@content_type_mapping
45
+ # We failed to match on the subtype
46
+ # Try to match only on the media type
47
+ pattern_media_type, pattern_extension, pattern_sub_type =
48
+ pattern.scan(mime_type_regexp)[0]
49
+ next if pattern_extension != nil || pattern_sub_type != nil
50
+ if media_type == pattern_media_type
51
+ return parser
52
+ end
53
+ end
54
+ end
55
+ return nil
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2010 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
+
16
+ require 'google/api_client/parsers/json_parser'
17
+
18
+ module Google
19
+ class APIClient
20
+ module JSON
21
+ ##
22
+ # A module which provides a parser for error responses.
23
+ class ErrorParser
24
+ include Google::APIClient::JSONParser
25
+
26
+ matches_fields 'error'
27
+
28
+ def error
29
+ return self['error']['message']
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright 2010 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
+
16
+ require 'google/api_client/parsers/json_parser'
17
+
18
+ module Google
19
+ class APIClient
20
+ module JSON
21
+ ##
22
+ # A module which provides a paginated parser.
23
+ module Pagination
24
+ def self.included(parser)
25
+ parser.class_eval do
26
+ include Google::APIClient::JSONParser
27
+ end
28
+ end
29
+
30
+ def next_page_token
31
+ return self["nextPageToken"]
32
+ end
33
+
34
+ def prev_page_token
35
+ return self["prevPageToken"]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -14,27 +14,105 @@
14
14
 
15
15
 
16
16
  require 'json'
17
+ require 'google/api_client/parser'
17
18
 
18
19
  module Google
19
20
  class APIClient
20
21
  ##
21
- # Provides a consistent interface by which to parse request and response
22
- # content.
23
- # TODO(mattpok): ensure floats, URLs, dates are parsed correctly
22
+ # Provides a module which all other parsers should include.
24
23
  module JSONParser
24
+ extend Parser
25
+ content_type 'application/json'
25
26
 
26
- def self.serialize(hash)
27
- # JSON parser used can accept arrays as well, but we will limit
28
- # to only allow hash to JSON string parsing to keep a simple interface
29
- unless hash.instance_of? Hash
30
- raise ArgumentError,
31
- "JSON generate expected a Hash but got a #{hash.class}."
27
+ module Matcher
28
+ def conditions
29
+ @conditions ||= []
30
+ end
31
+
32
+ def matches_kind(kind)
33
+ self.matches_field_value(:kind, kind)
34
+ end
35
+
36
+ def matches_fields(fields)
37
+ self.conditions << [:fields, fields]
38
+ end
39
+
40
+ def matches_field_value(field, value)
41
+ self.conditions << [:field_value, field, value]
32
42
  end
33
- return JSON.generate(hash)
34
43
  end
35
44
 
36
- def self.parse(json_string)
37
- return JSON.parse(json_string)
45
+ def self.parsers
46
+ @parsers ||= []
47
+ end
48
+
49
+ ##
50
+ # This method ensures that all parsers auto-register themselves.
51
+ def self.included(parser)
52
+ self.parsers << parser
53
+ parser.extend(Matcher)
54
+ end
55
+
56
+ def initialize(data)
57
+ @data = data.kind_of?(Hash) ? data : ::JSON.parse(data)
58
+ end
59
+
60
+ def [](key)
61
+ return self.json[key]
62
+ end
63
+
64
+ def json
65
+ if @data
66
+ data = @data
67
+ elsif self.respond_to?(:data)
68
+ data = self.data
69
+ else
70
+ raise TypeError, "Parser did not provide access to raw data."
71
+ end
72
+ return data
73
+ end
74
+
75
+ ##
76
+ # Matches a parser to the data.
77
+ def self.match(data)
78
+ for parser in self.parsers
79
+ conditions_met = true
80
+ for condition in (parser.conditions.sort_by { |c| c.size }).reverse
81
+ condition_type, *params = condition
82
+ case condition_type
83
+ when :fields
84
+ for field in params
85
+ if !data.has_key?(field)
86
+ conditions_met = false
87
+ break
88
+ end
89
+ end
90
+ when :field_values
91
+ field, value = params
92
+ if data[field] != value
93
+ conditions_met = false
94
+ break
95
+ end
96
+ else
97
+ raise ArgumentError, "Unknown condition type: #{condition_type}"
98
+ end
99
+ break if !conditions_met
100
+ end
101
+ if conditions_met
102
+ return parser
103
+ end
104
+ end
105
+ return nil
106
+ end
107
+
108
+ def self.parse(json)
109
+ data = json.kind_of?(Hash) ? json : ::JSON.parse(json)
110
+ parser = self.match(data)
111
+ if parser
112
+ return parser.new(data)
113
+ else
114
+ return data
115
+ end
38
116
  end
39
117
  end
40
118
  end