c169b9c6da6ef5fa2bd936481e90858db4831e5a
[arvados.git] / lib / google / api_client / result.rb
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 module Google
17   class APIClient
18     ##
19     # This class wraps a result returned by an API call.
20     class Result
21       def initialize(reference, request, response)
22         @reference = reference
23         @request = request
24         @response = response
25       end
26
27       attr_reader :reference
28
29       attr_reader :request
30
31       attr_reader :response
32
33       def status
34         return @response.status
35       end
36
37       def headers
38         return @response.headers
39       end
40
41       def body
42         return @response.body
43       end
44
45       def resumable_upload
46         @media_upload ||= Google::APIClient::ResumableUpload.new(self, reference.media, self.headers['location'])
47       end
48       
49       def media_type
50         _, content_type = self.headers.detect do |h, v|
51           h.downcase == 'Content-Type'.downcase
52         end
53         content_type[/^([^;]*);?.*$/, 1].strip.downcase
54       end
55       
56       def error?
57         return self.response.status >= 400
58       end
59
60       def success?
61         return !self.error?
62       end
63       
64       def error_message
65         if self.data?
66           if self.data.respond_to?(:error) &&
67              self.data.error.respond_to?(:message)
68             # You're going to get a terrible error message if the response isn't
69             # parsed successfully as an error.
70             return self.data.error.message
71           elsif self.data['error'] && self.data['error']['message']
72             return self.data['error']['message']
73           end
74         end
75         return self.body
76       end
77       
78       def data?
79         self.media_type == 'application/json'
80       end
81       
82       def data
83         return @data ||= (begin
84           media_type = self.media_type
85           data = self.body
86           case media_type
87           when 'application/json'
88             data = MultiJson.load(data)
89             # Strip data wrapper, if present
90             data = data['data'] if data.has_key?('data')
91           else
92             raise ArgumentError,
93               "Content-Type not supported for parsing: #{media_type}"
94           end
95           if @reference.api_method && @reference.api_method.response_schema
96             # Automatically parse using the schema designated for the
97             # response of this API method.
98             data = @reference.api_method.response_schema.new(data)
99             data
100           else
101             # Otherwise, return the raw unparsed value.
102             # This value must be indexable like a Hash.
103             data
104           end
105         end)
106       end
107
108       def pagination_type
109         return :token
110       end
111
112       def page_token_param
113         return "pageToken"
114       end
115
116       def next_page_token
117         if self.data.respond_to?(:next_page_token)
118           return self.data.next_page_token
119         elsif self.data.respond_to?(:[])
120           return self.data["nextPageToken"]
121         else
122           raise TypeError, "Data object did not respond to #next_page_token."
123         end
124       end
125
126       def next_page
127         merged_parameters = Hash[self.reference.parameters].merge({
128           self.page_token_param => self.next_page_token
129         })
130         # Because References can be coerced to Hashes, we can merge them,
131         # preserving all context except the API method parameters that we're
132         # using for pagination.
133         return Google::APIClient::Reference.new(
134           Hash[self.reference].merge(:parameters => merged_parameters)
135         )
136       end
137
138       def prev_page_token
139         if self.data.respond_to?(:prev_page_token)
140           return self.data.prev_page_token
141         elsif self.data.respond_to?(:[])
142           return self.data["prevPageToken"]
143         else
144           raise TypeError, "Data object did not respond to #next_page_token."
145         end
146       end
147
148       def prev_page
149         merged_parameters = Hash[self.reference.parameters].merge({
150           self.page_token_param => self.prev_page_token
151         })
152         # Because References can be coerced to Hashes, we can merge them,
153         # preserving all context except the API method parameters that we're
154         # using for pagination.
155         return Google::APIClient::Reference.new(
156           Hash[self.reference].merge(:parameters => merged_parameters)
157         )
158       end
159     end
160   end
161 end