cloudenvoy 0.1.0.dev → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +41 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +1 -0
  5. data/Appraisals +25 -0
  6. data/CHANGELOG.md +32 -0
  7. data/Gemfile.lock +215 -1
  8. data/README.md +581 -7
  9. data/app/controllers/cloudenvoy/application_controller.rb +8 -0
  10. data/app/controllers/cloudenvoy/subscriber_controller.rb +59 -0
  11. data/cloudenvoy.gemspec +15 -2
  12. data/config/routes.rb +5 -0
  13. data/examples/rails/.ruby-version +1 -0
  14. data/examples/rails/Gemfile +15 -0
  15. data/examples/rails/Gemfile.lock +207 -0
  16. data/examples/rails/Procfile +1 -0
  17. data/examples/rails/README.md +31 -0
  18. data/examples/rails/Rakefile +8 -0
  19. data/examples/rails/app/assets/config/manifest.js +2 -0
  20. data/examples/rails/app/assets/images/.keep +0 -0
  21. data/examples/rails/app/assets/stylesheets/application.css +15 -0
  22. data/examples/rails/app/channels/application_cable/channel.rb +6 -0
  23. data/examples/rails/app/channels/application_cable/connection.rb +6 -0
  24. data/examples/rails/app/controllers/application_controller.rb +4 -0
  25. data/examples/rails/app/controllers/concerns/.keep +0 -0
  26. data/examples/rails/app/helpers/application_helper.rb +4 -0
  27. data/examples/rails/app/javascript/packs/application.js +15 -0
  28. data/examples/rails/app/jobs/application_job.rb +9 -0
  29. data/examples/rails/app/mailers/application_mailer.rb +6 -0
  30. data/examples/rails/app/models/application_record.rb +5 -0
  31. data/examples/rails/app/models/concerns/.keep +0 -0
  32. data/examples/rails/app/publishers/hello_publisher.rb +34 -0
  33. data/examples/rails/app/subscribers/hello_subscriber.rb +16 -0
  34. data/examples/rails/app/views/layouts/application.html.erb +14 -0
  35. data/examples/rails/app/views/layouts/mailer.html.erb +13 -0
  36. data/examples/rails/app/views/layouts/mailer.text.erb +1 -0
  37. data/examples/rails/bin/rails +6 -0
  38. data/examples/rails/bin/rake +6 -0
  39. data/examples/rails/bin/setup +35 -0
  40. data/examples/rails/config.ru +7 -0
  41. data/examples/rails/config/application.rb +19 -0
  42. data/examples/rails/config/boot.rb +7 -0
  43. data/examples/rails/config/cable.yml +10 -0
  44. data/examples/rails/config/credentials.yml.enc +1 -0
  45. data/examples/rails/config/database.yml +25 -0
  46. data/examples/rails/config/environment.rb +7 -0
  47. data/examples/rails/config/environments/development.rb +65 -0
  48. data/examples/rails/config/environments/production.rb +114 -0
  49. data/examples/rails/config/environments/test.rb +50 -0
  50. data/examples/rails/config/initializers/application_controller_renderer.rb +9 -0
  51. data/examples/rails/config/initializers/assets.rb +14 -0
  52. data/examples/rails/config/initializers/backtrace_silencers.rb +8 -0
  53. data/examples/rails/config/initializers/cloudenvoy.rb +22 -0
  54. data/examples/rails/config/initializers/content_security_policy.rb +29 -0
  55. data/examples/rails/config/initializers/cookies_serializer.rb +7 -0
  56. data/examples/rails/config/initializers/filter_parameter_logging.rb +6 -0
  57. data/examples/rails/config/initializers/inflections.rb +17 -0
  58. data/examples/rails/config/initializers/mime_types.rb +5 -0
  59. data/examples/rails/config/initializers/wrap_parameters.rb +16 -0
  60. data/examples/rails/config/locales/en.yml +33 -0
  61. data/examples/rails/config/master.key +1 -0
  62. data/examples/rails/config/puma.rb +37 -0
  63. data/examples/rails/config/routes.rb +4 -0
  64. data/examples/rails/config/spring.rb +8 -0
  65. data/examples/rails/config/storage.yml +34 -0
  66. data/examples/rails/db/development.sqlite3 +0 -0
  67. data/examples/rails/db/test.sqlite3 +0 -0
  68. data/examples/rails/lib/assets/.keep +0 -0
  69. data/examples/rails/log/.keep +0 -0
  70. data/examples/rails/public/404.html +67 -0
  71. data/examples/rails/public/422.html +67 -0
  72. data/examples/rails/public/500.html +66 -0
  73. data/examples/rails/public/apple-touch-icon-precomposed.png +0 -0
  74. data/examples/rails/public/apple-touch-icon.png +0 -0
  75. data/examples/rails/public/favicon.ico +0 -0
  76. data/examples/rails/storage/.keep +0 -0
  77. data/gemfiles/rails_5.2.gemfile +7 -0
  78. data/gemfiles/rails_5.2.gemfile.lock +251 -0
  79. data/gemfiles/rails_6.0.gemfile +7 -0
  80. data/gemfiles/rails_6.0.gemfile.lock +267 -0
  81. data/gemfiles/semantic_logger_3.4.gemfile +7 -0
  82. data/gemfiles/semantic_logger_3.4.gemfile.lock +265 -0
  83. data/gemfiles/semantic_logger_4.6.gemfile +7 -0
  84. data/gemfiles/semantic_logger_4.6.gemfile.lock +265 -0
  85. data/gemfiles/semantic_logger_4.7.0.gemfile +7 -0
  86. data/gemfiles/semantic_logger_4.7.0.gemfile.lock +265 -0
  87. data/gemfiles/semantic_logger_4.7.2.gemfile +7 -0
  88. data/gemfiles/semantic_logger_4.7.2.gemfile.lock +265 -0
  89. data/lib/cloudenvoy.rb +96 -2
  90. data/lib/cloudenvoy/authentication_error.rb +6 -0
  91. data/lib/cloudenvoy/authenticator.rb +57 -0
  92. data/lib/cloudenvoy/backend/google_pub_sub.rb +146 -0
  93. data/lib/cloudenvoy/backend/memory_pub_sub.rb +89 -0
  94. data/lib/cloudenvoy/config.rb +165 -0
  95. data/lib/cloudenvoy/engine.rb +20 -0
  96. data/lib/cloudenvoy/invalid_subscriber_error.rb +6 -0
  97. data/lib/cloudenvoy/logger_wrapper.rb +167 -0
  98. data/lib/cloudenvoy/message.rb +96 -0
  99. data/lib/cloudenvoy/middleware/chain.rb +250 -0
  100. data/lib/cloudenvoy/pub_sub_client.rb +76 -0
  101. data/lib/cloudenvoy/publisher.rb +211 -0
  102. data/lib/cloudenvoy/publisher_logger.rb +32 -0
  103. data/lib/cloudenvoy/subscriber.rb +222 -0
  104. data/lib/cloudenvoy/subscriber_logger.rb +26 -0
  105. data/lib/cloudenvoy/subscription.rb +19 -0
  106. data/lib/cloudenvoy/testing.rb +106 -0
  107. data/lib/cloudenvoy/topic.rb +19 -0
  108. data/lib/cloudenvoy/version.rb +1 -1
  109. data/lib/tasks/cloudenvoy.rake +61 -0
  110. metadata +263 -6
@@ -1,8 +1,102 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/string/inflections'
4
+
3
5
  require 'cloudenvoy/version'
6
+ require 'cloudenvoy/config'
7
+
8
+ require 'cloudenvoy/authentication_error'
9
+ require 'cloudenvoy/invalid_subscriber_error'
10
+
11
+ require 'cloudenvoy/middleware/chain'
12
+ require 'cloudenvoy/authenticator'
13
+ require 'cloudenvoy/topic'
14
+ require 'cloudenvoy/subscription'
15
+ require 'cloudenvoy/pub_sub_client'
16
+ require 'cloudenvoy/logger_wrapper'
17
+ require 'cloudenvoy/publisher_logger'
18
+ require 'cloudenvoy/subscriber_logger'
19
+ require 'cloudenvoy/message'
20
+ require 'cloudenvoy/publisher'
21
+ require 'cloudenvoy/subscriber'
4
22
 
23
+ # Define and manage Cloud Pub/Sub publishers and subscribers
5
24
  module Cloudenvoy
6
- class Error < StandardError; end
7
- # Your code goes here...
25
+ attr_writer :config
26
+
27
+ #
28
+ # Cloudenvoy configurator.
29
+ #
30
+ def self.configure
31
+ yield(config)
32
+ end
33
+
34
+ #
35
+ # Return the Cloudenvoy configuration.
36
+ #
37
+ # @return [Cloudenvoy::Config] The Cloudenvoy configuration.
38
+ #
39
+ def self.config
40
+ @config ||= Config.new
41
+ end
42
+
43
+ #
44
+ # Return the Cloudenvoy logger.
45
+ #
46
+ # @return [Logger] The Cloudenvoy logger.
47
+ #
48
+ def self.logger
49
+ config.logger
50
+ end
51
+
52
+ #
53
+ # Publish a message to a topic. Shorthand method to Cloudenvoy::PubSubClient#publish.
54
+ #
55
+ # @param [String] topic The name of the topic
56
+ # @param [Hash, String] payload The message content.
57
+ # @param [Hash] attrs The message attributes.
58
+ #
59
+ # @return [Cloudenvoy::Message] The created message.
60
+ #
61
+ def self.publish(topic, payload, attrs = {})
62
+ PubSubClient.publish(topic, payload, attrs)
63
+ end
64
+
65
+ #
66
+ # Return the list of registered publishers.
67
+ #
68
+ # @return [Set<Cloudenvoy::Subscriber>] The list of registered publishers.
69
+ #
70
+ def self.publishers
71
+ @publishers ||= Set.new
72
+ end
73
+
74
+ #
75
+ # Return the list of registered subscribers.
76
+ #
77
+ # @return [Set<Cloudenvoy::Subscriber>] The list of registered subscribers.
78
+ #
79
+ def self.subscribers
80
+ @subscribers ||= Set.new
81
+ end
82
+
83
+ #
84
+ # Create/update subscriptions for all registered subscribers.
85
+ #
86
+ # @return [Array<Cloudenvoy::Subscription>] The upserted subscriptions.
87
+ #
88
+ def self.setup_subscribers
89
+ subscribers.flat_map(&:setup)
90
+ end
91
+
92
+ #
93
+ # Create/update default topics for all registered publishers.
94
+ #
95
+ # @return [Array<Cloudenvoy::Subscription>] The upserted topics.
96
+ #
97
+ def self.setup_publishers
98
+ publishers.flat_map(&:setup)
99
+ end
8
100
  end
101
+
102
+ require 'cloudenvoy/engine' if defined?(::Rails::Engine)
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudenvoy
4
+ class AuthenticationError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt'
4
+
5
+ module Cloudenvoy
6
+ # Manage token generation and verification
7
+ module Authenticator
8
+ module_function
9
+
10
+ # Algorithm used to sign the verification token
11
+ JWT_ALG = 'HS256'
12
+
13
+ #
14
+ # Return the cloudenvoy configuration. See Cloudenvoy#configure.
15
+ #
16
+ # @return [Cloudenvoy::Config] The library configuration.
17
+ #
18
+ def config
19
+ Cloudenvoy.config
20
+ end
21
+
22
+ #
23
+ # A Json Web Token (JWT) which is embedded as part of the receiving endpoint
24
+ # and will be used by the processor to authenticate the source of the message.
25
+ #
26
+ # @return [String] The jwt token
27
+ #
28
+ def verification_token
29
+ JWT.encode({ iat: Time.now.to_i }, config.secret, JWT_ALG)
30
+ end
31
+
32
+ #
33
+ # Verify a bearer token (jwt token)
34
+ #
35
+ # @param [String] bearer_token The token to verify.
36
+ #
37
+ # @return [Boolean] Return true if the token is valid
38
+ #
39
+ def verify(bearer_token)
40
+ JWT.decode(bearer_token, config.secret)
41
+ rescue JWT::VerificationError, JWT::DecodeError
42
+ false
43
+ end
44
+
45
+ #
46
+ # Verify a bearer token and raise a `Cloudenvoy::AuthenticationError`
47
+ # if the token is invalid.
48
+ #
49
+ # @param [String] bearer_token The token to verify.
50
+ #
51
+ # @return [Boolean] Return true if the token is valid
52
+ #
53
+ def verify!(bearer_token)
54
+ verify(bearer_token) || raise(AuthenticationError)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/cloud/pubsub'
4
+
5
+ module Cloudenvoy
6
+ module Backend
7
+ # Interface to GCP Pub/Sub and Pub/Sub local emulator
8
+ module GooglePubSub
9
+ module_function
10
+
11
+ #
12
+ # Return the cloudenvoy configuration. See Cloudenvoy#configure.
13
+ #
14
+ # @return [Cloudenvoy::Config] The library configuration.
15
+ #
16
+ def config
17
+ Cloudenvoy.config
18
+ end
19
+
20
+ #
21
+ # Return true if the current config mode is development.
22
+ #
23
+ # @return [Boolean] True if Cloudenvoy is run in development mode.
24
+ #
25
+ def development?
26
+ config.mode == :development
27
+ end
28
+
29
+ #
30
+ # Return the backend to use for sending messages.
31
+ #
32
+ # @return [Google::Cloud::Pub] The low level client instance.
33
+ #
34
+ def backend
35
+ @backend ||= Google::Cloud::PubSub.new({
36
+ project_id: config.gcp_project_id,
37
+ emulator_host: development? ? Cloudenvoy::Config::EMULATOR_HOST : nil
38
+ }.compact)
39
+ end
40
+
41
+ #
42
+ # Return an authenticated endpoint for processing Pub/Sub webhooks.
43
+ #
44
+ # @return [String] An authenticated endpoint.
45
+ #
46
+ def webhook_url
47
+ "#{config.processor_url}?token=#{Authenticator.verification_token}"
48
+ end
49
+
50
+ #
51
+ # Publish a message to a topic.
52
+ #
53
+ # @param [String] topic The name of the topic
54
+ # @param [Hash, String] payload The message content.
55
+ # @param [Hash] metadata The message attributes.
56
+ #
57
+ # @return [Cloudenvoy::Message] The created message.
58
+ #
59
+ def publish(topic, payload, metadata = {})
60
+ # Retrieve the topic
61
+ ps_topic = backend.topic(topic, skip_lookup: true)
62
+
63
+ # Publish the message
64
+ ps_msg = ps_topic.publish(payload.to_json, metadata.to_h)
65
+
66
+ # Return formatted message
67
+ Message.new(
68
+ id: ps_msg.message_id,
69
+ payload: payload,
70
+ metadata: metadata,
71
+ topic: topic
72
+ )
73
+ end
74
+
75
+ #
76
+ # Create or update a subscription for a specific topic.
77
+ #
78
+ # @param [String] topic The name of the topic
79
+ # @param [String] name The name of the subscription
80
+ # @param [Hash] opts The subscription configuration options
81
+ # @option opts [Integer] :deadline The maximum number of seconds after a subscriber receives a message
82
+ # before the subscriber should acknowledge the message.
83
+ # @option opts [Boolean] :retain_acked Indicates whether to retain acknowledged messages. If true,
84
+ # then messages are not expunged from the subscription's backlog, even if they are acknowledged,
85
+ # until they fall out of the retention window. Default is false.
86
+ # @option opts [<Type>] :retention How long to retain unacknowledged messages in the subscription's
87
+ # backlog, from the moment a message is published. If retain_acked is true, then this also configures
88
+ # the retention of acknowledged messages, and thus configures how far back in time a Subscription#seek
89
+ # can be done. Cannot be more than 604,800 seconds (7 days) or less than 600 seconds (10 minutes).
90
+ # Default is 604,800 seconds (7 days).
91
+ # @option opts [String] :filter An expression written in the Cloud Pub/Sub filter language.
92
+ # If non-empty, then only Message instances whose attributes field matches the filter are delivered
93
+ # on this subscription. If empty, then no messages are filtered out. Optional.
94
+ #
95
+ # @return [Cloudenvoy::Subscription] The upserted subscription.
96
+ #
97
+ def upsert_subscription(topic, name, opts = {})
98
+ sub_config = opts.to_h.merge(endpoint: webhook_url)
99
+
100
+ # Auto-create topic in development. In non-development environments
101
+ # the create subscription action raises an error if the topic does
102
+ # not exist
103
+ upsert_topic(topic) if development?
104
+
105
+ # Create subscription
106
+ ps_sub =
107
+ begin
108
+ # Retrieve the topic
109
+ ps_topic = backend.topic(topic, skip_lookup: true)
110
+
111
+ # Attempt to create the subscription
112
+ ps_topic.subscribe(name, sub_config)
113
+ rescue Google::Cloud::AlreadyExistsError
114
+ # Update endpoint on subscription
115
+ # Topic is not updated as it is name-dependent
116
+ backend.subscription(name).tap do |e|
117
+ sub_config.each do |k, v|
118
+ e.send("#{k}=", v)
119
+ end
120
+ end
121
+ end
122
+
123
+ # Return formatted subscription
124
+ Subscription.new(name: ps_sub.name, original: ps_sub)
125
+ end
126
+
127
+ #
128
+ # Create or update a topic.
129
+ #
130
+ # @param [String] topic The topic name.
131
+ #
132
+ # @return [Cloudenvoy::Topic] The upserted topic.
133
+ #
134
+ def upsert_topic(topic)
135
+ ps_topic = begin
136
+ backend.create_topic(topic)
137
+ rescue Google::Cloud::AlreadyExistsError
138
+ backend.topic(topic)
139
+ end
140
+
141
+ # Return formatted subscription
142
+ Topic.new(name: ps_topic.name, original: ps_topic)
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/cloud/pubsub'
4
+
5
+ module Cloudenvoy
6
+ module Backend
7
+ # Store messages in a memory queue. Used for testing
8
+ module MemoryPubSub
9
+ module_function
10
+
11
+ #
12
+ # Return the message queue for a specific topic.
13
+ #
14
+ # @param [String] name The topic to retrieve.
15
+ #
16
+ # @return [Array] The list of messages for the provided topic
17
+ #
18
+ def queue(topic)
19
+ @queues ||= {}
20
+ @queues[topic.to_s] ||= []
21
+ end
22
+
23
+ #
24
+ # Clear all messages in a specific topic.
25
+ #
26
+ # @param [String] name The topic to clear.
27
+ #
28
+ # @return [Array] The cleared array.
29
+ #
30
+ def clear(topic)
31
+ queue(topic).clear
32
+ end
33
+
34
+ #
35
+ # Clear all messages across all topics.
36
+ #
37
+ # @param [String] name The topic to clear.
38
+ #
39
+ def clear_all
40
+ @queues&.values&.each { |e| e.clear }
41
+ end
42
+
43
+ #
44
+ # Publish a message to a topic.
45
+ #
46
+ # @param [String] topic The name of the topic
47
+ # @param [Hash, String] payload The message content.
48
+ # @param [Hash] attrs The message attributes.
49
+ #
50
+ # @return [Cloudenvoy::Message] The created message.
51
+ #
52
+ def publish(topic, payload, metadata = {})
53
+ msg = Message.new(
54
+ id: SecureRandom.uuid,
55
+ payload: payload,
56
+ metadata: metadata,
57
+ topic: topic
58
+ )
59
+ queue(topic).push(msg)
60
+
61
+ msg
62
+ end
63
+
64
+ #
65
+ # Create or update a subscription for a specific topic.
66
+ #
67
+ # @param [String] topic The name of the topic
68
+ # @param [String] name The name of the subscription
69
+ # @param [Hash] opts The subscription configuration options
70
+ #
71
+ # @return [Cloudenvoy::Subscription] The upserted subscription.
72
+ #
73
+ def upsert_subscription(_topic, name, _opts)
74
+ Subscription.new(name: name)
75
+ end
76
+
77
+ #
78
+ # Create or update a topic.
79
+ #
80
+ # @param [String] topic The topic name.
81
+ #
82
+ # @return [Cloudenvoy::Topic] The upserted topic.
83
+ #
84
+ def upsert_topic(topic)
85
+ Topic.new(name: topic)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Cloudenvoy
6
+ # Holds cloudenvoy configuration. See Cloudenvoy#configure
7
+ class Config
8
+ attr_writer :secret, :gcp_project_id,
9
+ :gcp_sub_prefix, :processor_path, :logger, :mode
10
+
11
+ # Emulator host
12
+ EMULATOR_HOST = ENV['PUBSUB_EMULATOR_HOST'] || 'localhost:8085'
13
+
14
+ # Default application path used for processing messages
15
+ DEFAULT_PROCESSOR_PATH = '/cloudenvoy/receive'
16
+
17
+ PROCESSOR_HOST_MISSING = <<~DOC
18
+ Missing host for processing.
19
+ Please specify a processor hostname in form of `https://some-public-dns.example.com`'
20
+ DOC
21
+ SUB_PREFIX_MISSING_ERROR = <<~DOC
22
+ Missing GCP subscription prefix.
23
+ Please specify a subscription prefix in the form of `my-app`.
24
+ DOC
25
+ PROJECT_ID_MISSING_ERROR = <<~DOC
26
+ Missing GCP project ID.
27
+ Please specify a project ID in the cloudenvoy configurator.
28
+ DOC
29
+ SECRET_MISSING_ERROR = <<~DOC
30
+ Missing cloudenvoy secret.
31
+ Please specify a secret in the cloudenvoy initializer or add Rails secret_key_base in your credentials
32
+ DOC
33
+
34
+ #
35
+ # The operating mode.
36
+ # - :production => send messages to GCP Pub/Sub
37
+ # - :development => send message to gcloud CLI Pub/Sub emulator
38
+ #
39
+ # @return [<Type>] <description>
40
+ #
41
+ def mode
42
+ @mode ||= environment == 'development' ? :development : :production
43
+ end
44
+
45
+ #
46
+ # Return the current environment.
47
+ #
48
+ # @return [String] The environment name.
49
+ #
50
+ def environment
51
+ ENV['CLOUDENVOY_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
52
+ end
53
+
54
+ #
55
+ # Return the Cloudenvoy logger.
56
+ #
57
+ # @return [Logger, any] The cloudenvoy logger.
58
+ #
59
+ def logger
60
+ @logger ||= defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT)
61
+ end
62
+
63
+ #
64
+ # Return the full URL of the processor. Message payloads will be sent
65
+ # to this URL.
66
+ #
67
+ # @return [String] The processor URL.
68
+ #
69
+ def processor_url
70
+ File.join(processor_host, processor_path)
71
+ end
72
+
73
+ #
74
+ # Set the processor host. In the context of Rails the host will
75
+ # also be added to the list of authorized Rails hosts.
76
+ #
77
+ # @param [String] val The processor host to set.
78
+ #
79
+ def processor_host=(val)
80
+ @processor_host = val
81
+
82
+ # Check if Rails supports host filtering
83
+ return unless val &&
84
+ defined?(Rails) &&
85
+ Rails.application.config.respond_to?(:hosts) &&
86
+ Rails.application.config.hosts&.any?
87
+
88
+ # Add processor host to the list of authorized hosts
89
+ Rails.application.config.hosts << val.gsub(%r{https?://}, '')
90
+ end
91
+
92
+ #
93
+ # The hostname of the application processing the messages. The hostname must
94
+ # be reachable from Cloud Pub/Sub.
95
+ #
96
+ # @return [String] The processor host.
97
+ #
98
+ def processor_host
99
+ @processor_host || raise(StandardError, PROCESSOR_HOST_MISSING)
100
+ end
101
+
102
+ #
103
+ # The path on the host when message payloads will be sent.
104
+ # Default to `/cloudenvoy/receive`
105
+ #
106
+ #
107
+ # @return [String] The processor path
108
+ #
109
+ def processor_path
110
+ @processor_path || DEFAULT_PROCESSOR_PATH
111
+ end
112
+
113
+ #
114
+ # Return the prefix used for queues.
115
+ #
116
+ # @return [String] The prefix used when creating subscriptions.
117
+ #
118
+ def gcp_sub_prefix
119
+ @gcp_sub_prefix || raise(StandardError, SUB_PREFIX_MISSING_ERROR)
120
+ end
121
+
122
+ #
123
+ # Return the GCP project ID.
124
+ #
125
+ # @return [String] The ID of the project where pub/sub messages are hosted.
126
+ #
127
+ def gcp_project_id
128
+ @gcp_project_id || raise(StandardError, PROJECT_ID_MISSING_ERROR)
129
+ end
130
+
131
+ #
132
+ # Return the secret to use to sign the verification tokens
133
+ # attached to messages.
134
+ #
135
+ # @return [String] The cloudenvoy secret
136
+ #
137
+ def secret
138
+ @secret || (
139
+ defined?(Rails) && Rails.application.credentials&.dig(:secret_key_base)
140
+ ) || raise(StandardError, SECRET_MISSING_ERROR)
141
+ end
142
+
143
+ #
144
+ # Return the chain of publisher middlewares.
145
+ #
146
+ # @return [Cloudenvoy::Middleware::Chain] The chain of middlewares.
147
+ #
148
+ def publisher_middleware
149
+ @publisher_middleware ||= Middleware::Chain.new
150
+ yield @publisher_middleware if block_given?
151
+ @publisher_middleware
152
+ end
153
+
154
+ #
155
+ # Return the chain of subscriber middlewares.
156
+ #
157
+ # @return [Cloudenvoy::Middleware::Chain] The chain of middlewares.
158
+ #
159
+ def subscriber_middleware
160
+ @subscriber_middleware ||= Middleware::Chain.new
161
+ yield @subscriber_middleware if block_given?
162
+ @subscriber_middleware
163
+ end
164
+ end
165
+ end