pubsub_client 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f957900bdf24e83c57067bb4e1d21c0010bf0e7d6aacc45da639c2250c7fa1b4
4
- data.tar.gz: 036d770cee6fa93d718bb5aa91236825eb8bfe510293a0e352c611c46645bed0
3
+ metadata.gz: 4efa5b8b5abeff4f0dd13ee6d945c8f733b0b3b46dac6528fcf4bd527a5f08a5
4
+ data.tar.gz: c69aa4f023b2af7fbfe44c7021650026b7a31d2e49f5e543741b213fbb53d005
5
5
  SHA512:
6
- metadata.gz: 6f52651a2b7b2f7279266a96850a84e4ca3639d454ce5f6fa437276b545c8c79e5d7fa99cddd760f9b0987c18739003eca20efbeadebe17e9fa794c042f8c3b5
7
- data.tar.gz: 8157eaf01bef5317607e77b687f107fa3579a1a4b251bc8e6d4922a600d177274436a9302130642de2f6f8d86f9f79ca6f42bed5b3a973b92f52874194e68d92
6
+ metadata.gz: c4d38c468281279004e4504fe4a01b31652937b57270b0d63ce5930da8fefc82ec5d1ad2c12f5d0921001e12c460d0fe9e808c5fdaf1c790f47d4556ee7ed4b7
7
+ data.tar.gz: a7586ca0100862cb137627e110ffcae41e46d775645ef88622c6e594746eac636f7e7be67cb68ebffebfd0c899a5e7bd9bd79281e72a2ed3534fd3b49c7817df
data/CHANGELOG.txt CHANGED
@@ -1,3 +1,32 @@
1
+ Version 2.0.0 2021-03-30
2
+ ------------------------
3
+ 172dc19 Add support for multiple clients
4
+ 3525193 Update subscriber example to include name of subscription instead of …
5
+
6
+ Version 1.5.0 2020-11-05
7
+ ------------------------
8
+ 6cab366 Do not require GAC when stubbed
9
+ dab08dd Update `Subscriber#on_error` documentation
10
+
11
+ Version 1.4.1 2020-10-26
12
+ ------------------------
13
+ d28d99f Pass published attributes all the way through the client
14
+
15
+ Version 1.4.0 2020-10-26
16
+ ------------------------
17
+ 60b8403 Publish attributes
18
+
19
+ Version 1.3.0 2020-10-23
20
+ ------------------------
21
+ e9e87f7 This adds the ability to subscribe to a Pub/Sub topic. Of note, we expose two additional configuration params:
22
+ - `concurrency`: the number of threads the subscriber will run to process messages (defaults to 8 threads)
23
+ - `auto_ack`: flag to auto ack messages (default is `true` and _will_ ack messages)
24
+
25
+ These changes come with a handful of useful checks:
26
+ - ensures credentials are configured prior to subscribing
27
+ - raises an error if the target subscription does not exist
28
+ - raises an error if attempting to subscribe to a topic that has already been subscribed to
29
+
1
30
  Version 1.2.0 2020-09-25
2
31
  ------------------------
3
32
  aede3ab Raise custom error if GOOGLE_APPLICATION_CREDENTIALS not set
data/README.md CHANGED
@@ -22,21 +22,25 @@ Or install it yourself as:
22
22
 
23
23
  In order to use this gem, the environment variable `GOOGLE_APPLICATION_CREDENTIALS` must be set and point to the credentials JSON file.
24
24
 
25
- If there are environments where setting up credentials is too burdensome and/or publishing messages is not desired, `PubsubClient` can be stubbed out with `PubsubClient.stub!`, e.g.
25
+ If there are environments where setting up credentials is too burdensome and/or publishing messages is not desired, `PubsubClient` can be stubbed out with `.stub!`, e.g.
26
26
 
27
27
  ```ruby
28
+ @client = PubsubClient.new
29
+
28
30
  if test_env?
29
- PubsubClient.stub!
31
+ @client.stub!
30
32
  end
31
33
  ```
32
34
 
33
35
  ## Usage
34
36
 
35
- To publish a message to Pub/Sub, call `PubsubClient.publish(message, 'the-topic')`. This method takes any serializable object as an argument and yields a result object to a block. The `result` object has a method `#succeeded?` that returns `true` if the message was successfully published, otherwise `false`. In the latter case, there is a method `#error` that returns the error.
37
+ ### Publishing
38
+
39
+ To publish a message to Pub/Sub, call `@client.publish(message, 'the-topic')`. This method takes any serializable object as an argument and yields a result object to a block. The `result` object has a method `#succeeded?` that returns `true` if the message was successfully published, otherwise `false`. In the latter case, there is a method `#error` that returns the error.
36
40
 
37
- ### Example
41
+ #### Example
38
42
  ```ruby
39
- PubsubClient.publish(message, 'some-topic') do |result|
43
+ @client.publish(message, 'some-topic') do |result|
40
44
  if result.succeeded?
41
45
  puts 'yay!'
42
46
  else
@@ -45,14 +49,54 @@ PubsubClient.publish(message, 'some-topic') do |result|
45
49
  end
46
50
  ```
47
51
 
52
+ ### Subscribing
53
+
54
+ To subscribe to a topic, a client must first get a handle to the subscriber object. After doing so, a call to `subscriber.listener` will yield two arguments: the data (most clients will only need this) and the full Pub/Sub message (for anything more robust). Documentation for the full message can be found [here](https://googleapis.dev/ruby/google-cloud-pubsub/latest/Google/Cloud/PubSub/ReceivedMessage.html).
55
+
56
+ Optionally, a client can choose to handle exceptions raised by the subscriber. If a client chooses to do so, the listener **must** be configured before `on_error` since the latter needs a handler to the underlying listener. Additionally, exceptions will only be raised when the work inside the block is happening on the same thread. For instance, if the block enqueues a background worker and that worker raises an error, it will not be handled by the `on_error` block.
57
+
58
+ #### Example
59
+ ```ruby
60
+ subscriber = @client.subscriber('some-subscription')
61
+
62
+ subscriber.listener(concurrency: 4, auto_ack: false) do |data, received_message|
63
+ # Most clients will only need the first yielded arg.
64
+ # It is the same as calling received_message.data
65
+ end
66
+
67
+ # Optional
68
+ subscriber.on_error do |ex|
69
+ # Do something with the exception.
70
+ end
71
+
72
+ subscriber.subscribe # This will sleep
73
+ ```
74
+
75
+ By default, the underlying subscriber will use a concurrency of `8` threads and will acknowledge all messages. If these defaults are acceptable to the client, no arguments need to be passed in the call to `listener`.
76
+ ```ruby
77
+ subscriber.listener do |data, received_message|
78
+ # Do something
79
+ end
80
+ ```
81
+
48
82
  ## Development
49
83
 
50
84
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
51
85
 
52
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
86
+ To install this gem onto your local machine, run `bundle exec rake install`.
53
87
 
54
88
  ## Contributing
55
89
 
90
+ To contribute, open a pull request against `main`. Note that once your changes have been made, you should _not_ manually modify the `version.rb` or `CHANGELOG` as these will get updated automatically as part of the release process.
91
+
92
+ To release a new version, after you have merged your changes into `main`:
93
+ 1. Run the `gem-release` script. This can be found [here](https://github.com/apartmentlist/scripts/blob/main/bin/gem-release).
94
+ ```
95
+ path/to/gem-release [major/minor/patch] "Short message with changes"
96
+ ```
97
+ Note that the `Short message with changes` is what gets reflected in the releases of the repo.
98
+ 1. Run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). This step will update the `version.rb` and `CHANGELOG` files.
99
+
56
100
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pubsub_client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/pubsub_client/blob/master/CODE_OF_CONDUCT.md).
57
101
 
58
102
 
data/lib/pubsub_client.rb CHANGED
@@ -1,26 +1,50 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pubsub_client/version'
2
4
  require 'pubsub_client/null_publisher_factory'
5
+ require 'pubsub_client/null_subscriber_factory'
3
6
  require 'pubsub_client/publisher_factory'
7
+ require 'pubsub_client/subscriber_factory'
4
8
 
5
- module PubsubClient
9
+ class PubsubClient
6
10
  Error = Class.new(StandardError)
7
11
  ConfigurationError = Class.new(Error)
8
12
  CredentialsError = Class.new(Error)
9
13
  InvalidTopicError = Class.new(Error)
14
+ InvalidSubscriptionError = Class.new(Error)
10
15
 
11
- class << self
12
- def stub!
13
- raise ConfigurationError, 'PubsubClient is already configured' if @publisher_factory
14
- @publisher_factory = NullPublisherFactory.new
15
- end
16
+ def stub!
17
+ raise ConfigurationError, 'PubsubClient is already configured' if @publisher_factory || @subscriber_factory
18
+
19
+ @publisher_factory = NullPublisherFactory.new
20
+ @subscriber_factory = NullSubscriberFactory.new
21
+ @stubbed = true
22
+ end
23
+
24
+ # @param subscription [String] - The name of the subscription to subscribe to.
25
+ def subscriber(subscription)
26
+ ensure_credentials!
27
+
28
+ @subscriber_factory ||= SubscriberFactory.new
29
+ @subscriber_factory.build(subscription)
30
+ end
31
+
32
+ def publish(message, topic, attributes = {}, &block)
33
+ ensure_credentials!
34
+
35
+ @publisher_factory ||= PublisherFactory.new
36
+ @publisher_factory.build(topic).publish(message, attributes, &block)
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :stubbed, :publisher_factory, :subscriber_factory
16
42
 
17
- def publish(message, topic, &block)
18
- unless ENV['GOOGLE_APPLICATION_CREDENTIALS']
19
- raise CredentialsError, 'GOOGLE_APPLICATION_CREDENTIALS must be set'
20
- end
43
+ def ensure_credentials!
44
+ return if defined?(stubbed) && stubbed
21
45
 
22
- @publisher_factory ||= PublisherFactory.new
23
- @publisher_factory.build(topic).publish(message, &block)
46
+ unless ENV['GOOGLE_APPLICATION_CREDENTIALS']
47
+ raise CredentialsError, 'GOOGLE_APPLICATION_CREDENTIALS must be set'
24
48
  end
25
49
  end
26
50
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PubsubClient
3
+ class PubsubClient
4
4
  # A null object to act as a publisher when clients are in dev or test
5
5
  class NullPublisher
6
6
  # This is required so that this publisher maintains the same contract
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'null_publisher'
4
4
 
5
- module PubsubClient
5
+ class PubsubClient
6
6
  # A null object to act as a publisher factory when clients are in dev or test
7
7
  class NullPublisherFactory
8
8
  def build(*)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PubsubClient
4
+ # A null object to act as a subscriber when clients are in dev or test
5
+ class NullSubscriber
6
+ # This adds a subset of the available methods on the
7
+ # Google::Cloud::PubSub::ReceivedMessage, which is what
8
+ # gets yielded by the subscription when configuring the listener.
9
+ # For a list of methods, see the following link:
10
+ # https://googleapis.dev/ruby/google-cloud-pubsub/latest/Google/Cloud/PubSub/ReceivedMessage.html
11
+ NullResult = Struct.new(:acknowledge!) do
12
+ def data
13
+ '{"key":"value"}'
14
+ end
15
+
16
+ def published_at
17
+ Time.now
18
+ end
19
+ end
20
+
21
+ def listener(*, &block)
22
+ res = NullResult.new
23
+ yield res.data, res
24
+ end
25
+
26
+ def subscribe
27
+ # no-op
28
+ end
29
+
30
+ def on_error(&block)
31
+ # no-op
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'null_subscriber'
4
+
5
+ class PubsubClient
6
+ # A null object to act as a subscriber factory when clients are in dev or test
7
+ class NullSubscriberFactory
8
+ def build(*)
9
+ NullSubscriber.new
10
+ end
11
+ end
12
+ end
@@ -2,14 +2,15 @@
2
2
 
3
3
  require 'google/cloud/pubsub'
4
4
 
5
- module PubsubClient
5
+ class PubsubClient
6
6
  class Publisher
7
+ # @param topic [Google::Cloud::PubSub::Topic]
7
8
  def initialize(topic)
8
9
  @topic = topic
9
10
  end
10
11
 
11
- def publish(message, &block)
12
- topic.publish_async(message, &block)
12
+ def publish(message, attributes = {}, &block)
13
+ topic.publish_async(message, attributes, &block)
13
14
  end
14
15
 
15
16
  def flush
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative 'publisher'
4
4
 
5
- module PubsubClient
5
+ class PubsubClient
6
6
  # Build and memoize the Publisher, accounting for GRPC's requirements around forking.
7
7
  class PublisherFactory
8
8
  def initialize
@@ -10,6 +10,8 @@ module PubsubClient
10
10
  @publishers = {}
11
11
  end
12
12
 
13
+ # @param topic_name [String]
14
+ # @return [Publisher]
13
15
  def build(topic_name)
14
16
  # GRPC fails when attempting to use a connection created in a process that gets
15
17
  # forked with the message
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/cloud/pubsub'
4
+
5
+ class PubsubClient
6
+ class Subscriber
7
+ DEFAULT_CONCURRENCY = 8
8
+
9
+ # @param subscription [Google::Cloud::PubSub::Subscription]
10
+ def initialize(subscription)
11
+ @subscription = subscription
12
+ end
13
+
14
+ # @param concurrency [Integer] - The number of threads to run the subscriber with. Default is 8.
15
+ # @param auto_ack [Boolean] - Flag to acknowledge the Pub/Sub message. A message must be acked
16
+ # to remove it from the topic. Default is `true`.
17
+ #
18
+ # @return [Google::Cloud::PubSub::Subscriber]
19
+ def listener(concurrency: DEFAULT_CONCURRENCY, auto_ack: true, &block)
20
+ @listener ||= begin
21
+ @subscription.listen(threads: { callback: concurrency }) do |received_message|
22
+ yield received_message.data, received_message
23
+ received_message.acknowledge! if auto_ack
24
+ end
25
+ end
26
+ end
27
+
28
+ def subscribe
29
+ raise ConfigurationError, 'A listener must be configured' unless @listener
30
+
31
+ begin
32
+ @listener.start
33
+
34
+ sleep
35
+ rescue SignalException
36
+ @listener.stop.wait!
37
+ end
38
+ end
39
+
40
+ def on_error(&block)
41
+ raise ConfigurationError, 'A listener must be configured' unless @listener
42
+
43
+ @listener.on_error do |exception|
44
+ yield exception
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'subscriber'
4
+
5
+ class PubsubClient
6
+ class SubscriberFactory
7
+ def initialize
8
+ @subscribers = {}
9
+ end
10
+
11
+ # @param subscription_name [String]
12
+ # @retrun [Subscriber]
13
+ def build(subscription_name)
14
+ if @subscribers.key?(subscription_name)
15
+ raise ConfigurationError, "PubsubClient already subscribed to #{subscription_name}"
16
+ end
17
+
18
+ @subscribers[subscription_name] = build_subscriber(subscription_name)
19
+ end
20
+
21
+ private
22
+
23
+ def build_subscriber(subscription_name)
24
+ pubsub = Google::Cloud::PubSub.new
25
+ subscription = pubsub.subscription(subscription_name)
26
+ raise InvalidSubscriptionError, "The subscription #{subscription_name} does not exist" unless subscription
27
+ Subscriber.new(subscription)
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
- module PubsubClient
2
- VERSION = '1.2.0'
1
+ class PubsubClient
2
+ VERSION = '2.0.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubsub_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Apartment List Platforms
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-29 00:00:00.000000000 Z
11
+ date: 2021-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -88,8 +88,12 @@ files:
88
88
  - lib/pubsub_client.rb
89
89
  - lib/pubsub_client/null_publisher.rb
90
90
  - lib/pubsub_client/null_publisher_factory.rb
91
+ - lib/pubsub_client/null_subscriber.rb
92
+ - lib/pubsub_client/null_subscriber_factory.rb
91
93
  - lib/pubsub_client/publisher.rb
92
94
  - lib/pubsub_client/publisher_factory.rb
95
+ - lib/pubsub_client/subscriber.rb
96
+ - lib/pubsub_client/subscriber_factory.rb
93
97
  - lib/pubsub_client/version.rb
94
98
  - pubsub_client.gemspec
95
99
  homepage: https://github.com/apartmentlist/pubsub_client