google-api-client 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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