gcloud 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,284 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "gcloud/version"
17
+ require "google/api_client"
18
+
19
+ module Gcloud
20
+ module Pubsub
21
+ ##
22
+ # Represents the connection to Pub/Sub,
23
+ # as well as expose the API calls.
24
+ class Connection #:nodoc:
25
+ API_VERSION = "v1"
26
+
27
+ attr_accessor :project
28
+ attr_accessor :credentials #:nodoc:
29
+
30
+ ##
31
+ # Creates a new Connection instance.
32
+ def initialize project, credentials
33
+ @project = project
34
+ @credentials = credentials
35
+ @client = Google::APIClient.new application_name: "gcloud-ruby",
36
+ application_version: Gcloud::VERSION
37
+ @client.authorization = @credentials.client
38
+ @pubsub = @client.discovered_api "pubsub", API_VERSION
39
+ end
40
+
41
+ ##
42
+ # Gets the configuration of a topic.
43
+ # Since the topic only has the name attribute,
44
+ # this method is only useful to check the existence of a topic.
45
+ # If other attributes are added in the future,
46
+ # they will be returned here.
47
+ def get_topic topic_name, options = {}
48
+ @client.execute(
49
+ api_method: @pubsub.projects.topics.get,
50
+ parameters: { topic: topic_path(topic_name, options) }
51
+ )
52
+ end
53
+
54
+ ##
55
+ # Creates the given topic with the given name.
56
+ def create_topic topic_name, options = {}
57
+ @client.execute(
58
+ api_method: @pubsub.projects.topics.create,
59
+ parameters: { name: topic_path(topic_name, options) }
60
+ )
61
+ end
62
+
63
+ ##
64
+ # Lists matching topics.
65
+ def list_topics options = {}
66
+ params = { project: project_path(options),
67
+ pageToken: options.delete(:token),
68
+ pageSize: options.delete(:max)
69
+ }.delete_if { |_, v| v.nil? }
70
+
71
+ @client.execute(
72
+ api_method: @pubsub.projects.topics.list,
73
+ parameters: params
74
+ )
75
+ end
76
+
77
+ ##
78
+ # Deletes the topic with the given name.
79
+ # All subscriptions to this topic are also deleted.
80
+ # Returns NOT_FOUND if the topic does not exist.
81
+ # After a topic is deleted, a new topic may be created with the same name.
82
+ def delete_topic topic
83
+ @client.execute(
84
+ api_method: @pubsub.projects.topics.delete,
85
+ parameters: { topic: topic }
86
+ )
87
+ end
88
+
89
+ def get_topic_policy topic_name, options = {}
90
+ @client.execute(
91
+ api_method: @pubsub.projects.topics.get_iam_policy,
92
+ parameters: { resource: topic_path(topic_name, options) }
93
+ )
94
+ end
95
+
96
+ def set_topic_policy topic_name, new_policy, options = {}
97
+ @client.execute(
98
+ api_method: @pubsub.projects.topics.set_iam_policy,
99
+ parameters: { resource: topic_path(topic_name, options) },
100
+ body_object: { policy: new_policy }
101
+ )
102
+ end
103
+
104
+ ##
105
+ # Creates a subscription on a given topic for a given subscriber.
106
+ def create_subscription topic, subscription_name, options = {}
107
+ data = subscription_data topic, options
108
+ @client.execute(
109
+ api_method: @pubsub.projects.subscriptions.create,
110
+ parameters: { name: subscription_path(subscription_name, options) },
111
+ body_object: data
112
+ )
113
+ end
114
+
115
+ ##
116
+ # Gets the details of a subscription.
117
+ def get_subscription subscription_name, options = {}
118
+ @client.execute(
119
+ api_method: @pubsub.projects.subscriptions.get,
120
+ parameters: {
121
+ subscription: subscription_path(subscription_name, options) }
122
+ )
123
+ end
124
+
125
+ ##
126
+ # Lists matching subscriptions by project.
127
+ def list_subscriptions options = {}
128
+ params = { project: project_path(options),
129
+ pageToken: options.delete(:token),
130
+ pageSize: options.delete(:max)
131
+ }.delete_if { |_, v| v.nil? }
132
+
133
+ @client.execute(
134
+ api_method: @pubsub.projects.subscriptions.list,
135
+ parameters: params
136
+ )
137
+ end
138
+
139
+ ##
140
+ # Lists matching subscriptions by project and topic.
141
+ def list_topics_subscriptions topic, options = {}
142
+ params = { topic: topic,
143
+ pageToken: options.delete(:token),
144
+ pageSize: options.delete(:max)
145
+ }.delete_if { |_, v| v.nil? }
146
+
147
+ @client.execute(
148
+ api_method: @pubsub.projects.topics.subscriptions.list,
149
+ parameters: params
150
+ )
151
+ end
152
+
153
+ ##
154
+ # Deletes an existing subscription.
155
+ # All pending messages in the subscription are immediately dropped.
156
+ def delete_subscription subscription
157
+ @client.execute(
158
+ api_method: @pubsub.projects.subscriptions.delete,
159
+ parameters: { subscription: subscription }
160
+ )
161
+ end
162
+
163
+ def get_subscription_policy subscription_name, options = {}
164
+ @client.execute(
165
+ api_method: @pubsub.projects.subscriptions.get_iam_policy,
166
+ parameters: {
167
+ resource: subscription_path(subscription_name, options) }
168
+ )
169
+ end
170
+
171
+ def set_subscription_policy subscription_name, new_policy, options = {}
172
+ @client.execute(
173
+ api_method: @pubsub.projects.subscriptions.set_iam_policy,
174
+ parameters: {
175
+ resource: subscription_path(subscription_name, options) },
176
+ body_object: { policy: new_policy }
177
+ )
178
+ end
179
+
180
+ ##
181
+ # Adds one or more messages to the topic.
182
+ # Returns NOT_FOUND if the topic does not exist.
183
+ # The messages parameter is an array of arrays.
184
+ # The first element is the data, second is attributes hash.
185
+ def publish topic, messages
186
+ gapi_msgs = messages.map do |data, attributes|
187
+ { data: [data].pack("m"), attributes: attributes }
188
+ end
189
+ @client.execute(
190
+ api_method: @pubsub.projects.topics.publish,
191
+ parameters: { topic: topic },
192
+ body_object: { messages: gapi_msgs }
193
+ )
194
+ end
195
+
196
+ ##
197
+ # Pulls a single message from the server.
198
+ def pull subscription, options = {}
199
+ body = { returnImmediately: !(!options.fetch(:immediate, true)),
200
+ maxMessages: options.fetch(:max, 100).to_i }
201
+
202
+ @client.execute(
203
+ api_method: @pubsub.projects.subscriptions.pull,
204
+ parameters: { subscription: subscription },
205
+ body_object: body
206
+ )
207
+ end
208
+
209
+ ##
210
+ # Acknowledges receipt of a message.
211
+ def acknowledge subscription, *ack_ids
212
+ @client.execute(
213
+ api_method: @pubsub.projects.subscriptions.acknowledge,
214
+ parameters: { subscription: subscription },
215
+ body_object: { ackIds: ack_ids }
216
+ )
217
+ end
218
+
219
+ ##
220
+ # Modifies the PushConfig for a specified subscription.
221
+ def modify_push_config subscription, endpoint, attributes
222
+ @client.execute(
223
+ api_method: @pubsub.projects.subscriptions.modify_push_config,
224
+ parameters: { subscription: subscription },
225
+ body_object: { pushConfig: { pushEndpoint: endpoint,
226
+ attributes: attributes } }
227
+ )
228
+ end
229
+
230
+ ##
231
+ # Modifies the ack deadline for a specific message.
232
+ def modify_ack_deadline subscription, ids, deadline
233
+ ids = Array ids
234
+ @client.execute(
235
+ api_method: @pubsub.projects.subscriptions.modify_ack_deadline,
236
+ parameters: { subscription: subscription },
237
+ body_object: { ackIds: ids, ackDeadlineSeconds: deadline }
238
+ )
239
+ end
240
+
241
+ def project_path options = {}
242
+ project_name = options[:project] || project
243
+ "projects/#{project_name}"
244
+ end
245
+
246
+ def topic_path topic_name, options = {}
247
+ return topic_name if topic_name.to_s.include? "/"
248
+ "#{project_path(options)}/topics/#{topic_name}"
249
+ end
250
+
251
+ def subscription_path subscription_name, options = {}
252
+ return subscription_name if subscription_name.to_s.include? "/"
253
+ "#{project_path(options)}/subscriptions/#{subscription_name}"
254
+ end
255
+
256
+ protected
257
+
258
+ def subscription_data topic, options = {}
259
+ deadline = options[:deadline]
260
+ endpoint = options[:endpoint]
261
+ attributes = hashify options[:attributes]
262
+
263
+ data = { topic: topic }
264
+ data[:ackDeadlineSeconds] = deadline if deadline
265
+ if endpoint
266
+ data[:pushConfig] = { pushEndpoint: endpoint,
267
+ attributes: attributes }
268
+ end
269
+ data
270
+ end
271
+
272
+ ##
273
+ # Make sure the object is converted to a hash
274
+ # Ruby 1.9.3 doesn't support to_h, so here we are.
275
+ def hashify hash
276
+ if hash.respond_to? :to_h
277
+ hash.to_h
278
+ else
279
+ Hash.try_convert(hash) || {}
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,29 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "gcloud/credentials"
17
+
18
+ module Gcloud
19
+ module Pubsub
20
+ ##
21
+ # Represents the OAuth 2.0 signing logic for Pub/Sub.
22
+ class Credentials < Gcloud::Credentials #:nodoc:
23
+ SCOPE = ["https://www.googleapis.com/auth/pubsub",
24
+ "https://www.googleapis.com/auth/cloud-platform"]
25
+ PATH_ENV_VARS = %w(PUBSUB_KEYFILE GOOGLE_CLOUD_KEYFILE)
26
+ JSON_ENV_VARS = %w(PUBSUB_KEYFILE_JSON GOOGLE_CLOUD_KEYFILE_JSON)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Gcloud
17
+ module Pubsub
18
+ ##
19
+ # = Storage Error
20
+ #
21
+ # Base Pub/Sub exception class.
22
+ class Error < Gcloud::Error
23
+ ##
24
+ # The response object of the failed HTTP request.
25
+ attr_reader :response
26
+
27
+ def self.from_response resp #:nodoc:
28
+ new.tap do |e|
29
+ e.response = resp
30
+ end
31
+ end
32
+ end
33
+
34
+ ##
35
+ # = ApiError
36
+ #
37
+ # Raised when an API call is not successful.
38
+ class ApiError < Error
39
+ ##
40
+ # The code of the error.
41
+ def code
42
+ response.data["error"]["code"]
43
+ rescue
44
+ nil
45
+ end
46
+
47
+ ##
48
+ # The errors encountered.
49
+ def errors
50
+ response.data["error"]["errors"]
51
+ rescue
52
+ []
53
+ end
54
+
55
+ def initialize message, response
56
+ super message
57
+ @response = response
58
+ end
59
+
60
+ def self.from_response resp #:nodoc:
61
+ klass = klass_for resp.data["error"]["status"]
62
+ klass.new resp.data["error"]["message"], resp
63
+ rescue
64
+ Gcloud::Pubsub::Error.from_response resp
65
+ end
66
+
67
+ def self.klass_for status
68
+ if status == "ALREADY_EXISTS"
69
+ return AlreadyExistsError
70
+ elsif status == "NOT_FOUND"
71
+ return NotFoundError
72
+ end
73
+ self
74
+ end
75
+ end
76
+
77
+ ##
78
+ # = AlreadyExistsError
79
+ #
80
+ # Raised when Pub/Sub returns an +ALREADY_EXISTS+ error.
81
+ class AlreadyExistsError < ApiError
82
+ end
83
+
84
+ ##
85
+ # = NotFoundError
86
+ #
87
+ # Raised when Pub/Sub returns a +NOT_FOUND+ error.
88
+ class NotFoundError < ApiError
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,90 @@
1
+ #--
2
+ # Copyright 2015 Google Inc. All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "gcloud/pubsub/errors"
17
+
18
+ module Gcloud
19
+ module Pubsub
20
+ ##
21
+ # = Message
22
+ #
23
+ # Represents a Pub/Sub Message.
24
+ #
25
+ # Message objects are created by Topic#publish.
26
+ # Subscription#pull returns an array of ReceivedMessage objects, each of
27
+ # which contains a Message object. Each ReceivedMessage object can be
28
+ # acknowledged and/or delayed.
29
+ #
30
+ # require "gcloud"
31
+ #
32
+ # gcloud = Gcloud.new
33
+ # pubsub = gcloud.pubsub
34
+ #
35
+ # # Publish a message
36
+ # topic = pubsub.topic "my-topic"
37
+ # message = topic.publish "new-message"
38
+ # puts message.data #=> "new-message"
39
+ #
40
+ # # Pull a message
41
+ # sub = pubsub.subscription "my-topic-sub"
42
+ # received_message = sub.pull.first
43
+ # puts received_message.message.data #=> "new-message"
44
+ #
45
+ class Message
46
+ ##
47
+ # The Google API Client object.
48
+ attr_accessor :gapi #:nodoc:
49
+
50
+ ##
51
+ # Create an empty Message object.
52
+ # This can be used to publish several messages in bulk.
53
+ def initialize data = nil, attributes = {}
54
+ @gapi = {}
55
+ @gapi["data"] = data
56
+ @gapi["attributes"] = attributes
57
+ end
58
+
59
+ ##
60
+ # The received data.
61
+ def data
62
+ @gapi["data"]
63
+ end
64
+
65
+ ##
66
+ # The received attributes.
67
+ def attributes
68
+ attrs = @gapi["attributes"]
69
+ attrs = attrs.to_hash if attrs.respond_to? :to_hash
70
+ attrs
71
+ end
72
+
73
+ ##
74
+ # The ID of this message, assigned by the server at publication time.
75
+ # Guaranteed to be unique within the topic.
76
+ def message_id
77
+ @gapi["messageId"]
78
+ end
79
+ alias_method :msg_id, :message_id
80
+
81
+ ##
82
+ # New Topic from a Google API Client object.
83
+ def self.from_gapi gapi #:nodoc:
84
+ new.tap do |f|
85
+ f.gapi = gapi
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end