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