Class: Webhookdb::ServiceIntegration
- Inherits:
-
Object
- Object
- Webhookdb::ServiceIntegration
- Defined in:
- lib/webhookdb/service_integration.rb
Defined Under Namespace
Classes: Stats, TableRenameError
Constant Summary collapse
- INTEGRATION_INFO_FIELDS =
We limit the information that a user can access through the CLI to these fields.
{ "id" => :opaque_id, "service" => :service_name, "table" => :table_name, "url" => :unauthed_webhook_endpoint, "webhook_secret" => :webhook_secret, "webhookdb_api_key" => :webhookdb_api_key, "api_url" => :api_url, }.freeze
Instance Attribute Summary collapse
-
#api_url ⇒ String
Root Url of the api to backfill from.
-
#backfill_key ⇒ String
Key for backfilling.
-
#backfill_secret ⇒ String
Password/secret for backfilling.
-
#data_encryption_secret ⇒ String
The encryption key used to encrypt data for this organization.
- #depends_on ⇒ Webhookdb::ServiceIntegration
- #opaque_id ⇒ String
- #organization ⇒ Webhookdb::Organization
-
#service_name ⇒ String
Lookup name of the service.
-
#skip_webhook_verification ⇒ Boolean
Set this to disable webhook verification on this integration.
-
#table_name ⇒ String
Name of the table.
-
#webhook_secret ⇒ String
Secret used to sign webhooks.
-
#webhookdb_api_key ⇒ String
API Key used in the Whdb-Api-Key header that can be used to identify this service integration (where the opaque id cannot be used), and is a secret so can be used for authentication.
Class Method Summary collapse
- .create_disambiguated(service_name, **kwargs) ⇒ Webhookdb::ServiceIntegration
- .for_api_key(key) ⇒ Webhookdb::ServiceIntegration
Instance Method Summary collapse
- #authed_api_path ⇒ Object
-
#before_create ⇒ Object
:Sequel Hooks:.
- #can_be_modified_by?(customer) ⇒ Boolean
-
#dependency_candidates ⇒ Array<Webhookdb::ServiceIntegration>
Return service integrations that can be used as the dependency for this integration.
- #destroy_self_and_all_dependents ⇒ Object
- #ensure_opaque_id ⇒ Object
- #ensure_sequence(skip_check: false) ⇒ Object
- #ensure_sequence_sql(skip_check: false) ⇒ Object
- #log_tags ⇒ Object
- #new_api_key ⇒ Object
- #new_opaque_id ⇒ Object
- #plan_supports_integration? ⇒ Boolean
- #recursive_dependents ⇒ Object
- #rename_table(to:) ⇒ Object
- #replicator ⇒ Webhookdb::Replicator::Base
- #requires_sequence? ⇒ Boolean
- #sequence_name ⇒ Object
- #sequence_nextval ⇒ Object
- #stats ⇒ Webhookdb::ServiceIntegration::Stats
- #unauthed_webhook_endpoint ⇒ Object
- #unauthed_webhook_path ⇒ Object
Instance Attribute Details
#api_url ⇒ String
Returns Root Url of the api to backfill from.
|
# File 'lib/webhookdb/service_integration.rb', line 303
|
#backfill_key ⇒ String
Returns Key for backfilling.
|
# File 'lib/webhookdb/service_integration.rb', line 306
|
#backfill_secret ⇒ String
Returns Password/secret for backfilling.
|
# File 'lib/webhookdb/service_integration.rb', line 309
|
#data_encryption_secret ⇒ String
Returns The encryption key used to encrypt data for this organization. Note that this field is itself encrypted using Sequel encryption; its decrypted value is meant to be used as the data encryption key.
|
# File 'lib/webhookdb/service_integration.rb', line 325
|
#opaque_id ⇒ String
|
# File 'lib/webhookdb/service_integration.rb', line 300
|
#service_name ⇒ String
Returns Lookup name of the service.
|
# File 'lib/webhookdb/service_integration.rb', line 297
|
#skip_webhook_verification ⇒ Boolean
Returns Set this to disable webhook verification on this integration. Useful when replaying logged webhooks.
|
# File 'lib/webhookdb/service_integration.rb', line 330
|
#table_name ⇒ String
Returns Name of the table.
|
# File 'lib/webhookdb/service_integration.rb', line 294
|
#webhook_secret ⇒ String
Returns Secret used to sign webhooks.
|
# File 'lib/webhookdb/service_integration.rb', line 312
|
#webhookdb_api_key ⇒ String
Returns API Key used in the Whdb-Api-Key header that can be used to identify this service integration (where the opaque id cannot be used), and is a secret so can be used for authentication. Need for this should be rare- it’s usually only used outside of the core webhookdb/backfill design like for two-way sync (Front Channel/Signalwire integration, for example).
|
# File 'lib/webhookdb/service_integration.rb', line 315
|
Class Method Details
.create_disambiguated(service_name, **kwargs) ⇒ Webhookdb::ServiceIntegration
76 77 78 79 |
# File 'lib/webhookdb/service_integration.rb', line 76 def self.create_disambiguated(service_name, **kwargs) kwargs[:table_name] ||= "#{service_name}_#{SecureRandom.hex(2)}" return self.create(service_name:, **kwargs) end |
.for_api_key(key) ⇒ Webhookdb::ServiceIntegration
82 83 84 |
# File 'lib/webhookdb/service_integration.rb', line 82 def self.for_api_key(key) return self.with_encrypted_value(:webhookdb_api_key, key).first end |
Instance Method Details
#authed_api_path ⇒ Object
104 105 106 |
# File 'lib/webhookdb/service_integration.rb', line 104 def authed_api_path return "/v1/organizations/#{self.organization_id}/service_integrations/#{self.opaque_id}" end |
#before_create ⇒ Object
:Sequel Hooks:
287 288 289 |
# File 'lib/webhookdb/service_integration.rb', line 287 def before_create self.ensure_opaque_id end |
#can_be_modified_by?(customer) ⇒ Boolean
86 87 88 |
# File 'lib/webhookdb/service_integration.rb', line 86 def can_be_modified_by?(customer) return customer.verified_member_of?(self.organization) end |
#dependency_candidates ⇒ Array<Webhookdb::ServiceIntegration>
Return service integrations that can be used as the dependency for this integration.
134 135 136 137 138 139 |
# File 'lib/webhookdb/service_integration.rb', line 134 def dependency_candidates dep_descr = self.replicator.descriptor.dependency_descriptor return [] if dep_descr.nil? return self.organization.service_integrations. select { |si| si.service_name == dep_descr.name } end |
#destroy_self_and_all_dependents ⇒ Object
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/webhookdb/service_integration.rb', line 145 def destroy_self_and_all_dependents self.dependents.each(&:destroy_self_and_all_dependents) begin self.replicator.admin_dataset(timeout: :fast) { |ds| ds.db << "DROP TABLE #{self.table_name}" } rescue Sequel::DatabaseError => e raise unless e.wrapped_exception.is_a?(PG::UndefinedTable) end self.destroy end |
#ensure_opaque_id ⇒ Object
273 |
# File 'lib/webhookdb/service_integration.rb', line 273 def ensure_opaque_id = self[:opaque_id] ||= self.new_opaque_id |
#ensure_sequence(skip_check: false) ⇒ Object
257 258 259 |
# File 'lib/webhookdb/service_integration.rb', line 257 def ensure_sequence(skip_check: false) self.db << self.ensure_sequence_sql(skip_check:) end |
#ensure_sequence_sql(skip_check: false) ⇒ Object
261 262 263 264 265 |
# File 'lib/webhookdb/service_integration.rb', line 261 def ensure_sequence_sql(skip_check: false) raise Webhookdb::InvalidPrecondition, "#{self.service_name} does not require sequence" if !skip_check && !self.requires_sequence? return "CREATE SEQUENCE IF NOT EXISTS #{self.sequence_name}" end |
#log_tags ⇒ Object
95 96 97 98 99 100 101 102 |
# File 'lib/webhookdb/service_integration.rb', line 95 def return { service_integration_id: self.id, service_integration_name: self.service_name, service_integration_table: self.table_name, **self.organization., } end |
#new_api_key ⇒ Object
275 276 277 278 279 280 281 |
# File 'lib/webhookdb/service_integration.rb', line 275 def new_api_key k = +"sk/" k << self.ensure_opaque_id k << "/" k << Webhookdb::Id.rand_enc(24) return k end |
#new_opaque_id ⇒ Object
271 |
# File 'lib/webhookdb/service_integration.rb', line 271 def new_opaque_id = Webhookdb::Id.new_opaque_id("svi") |
#plan_supports_integration? ⇒ Boolean
116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/webhookdb/service_integration.rb', line 116 def plan_supports_integration? # if the sint's organization has an active subscription, return true return true if self.organization.active_subscription? # if there is no active subscription, check whether the integration is one of the first two # created by the organization limit = Webhookdb::Subscription.max_free_integrations free_integrations = Webhookdb::ServiceIntegration. where(organization: self.organization).order(:created_at, :id).limit(limit).all free_integrations.each do |sint| return true if sint.id == self.id end # if not, the integration is not supported return false end |
#recursive_dependents ⇒ Object
141 142 143 |
# File 'lib/webhookdb/service_integration.rb', line 141 def recursive_dependents return self.dependents + self.dependents.flat_map(&:recursive_dependents) end |
#rename_table(to:) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/webhookdb/service_integration.rb', line 223 def rename_table(to:) Webhookdb::Organization::DatabaseMigration.guard_ongoing!(self.organization) unless Webhookdb::DBAdapter::VALID_IDENTIFIER.match?(to) msg = "Sorry, this is not a valid table name. " + Webhookdb::DBAdapter::INVALID_IDENTIFIER_MESSAGE msg += " And we see you what you did there ;)" if to.include?(";") && to.downcase.include?("drop") raise TableRenameError, msg end self.db.transaction do begin self.organization.admin_connection { |db| db << "ALTER TABLE #{self.table_name} RENAME TO #{to}" } rescue Sequel::DatabaseError => e case e.wrapped_exception when PG::DuplicateTable raise TableRenameError, "There is already a table named \"#{to}\". Run `webhookdb db tables` to see available tables." when PG::SyntaxError raise TableRenameError, "Please try again with double quotes around '#{to}' since it contains invalid identifier characters." else raise e end end self.update(table_name: to) end end |
#replicator ⇒ Webhookdb::Replicator::Base
91 92 93 |
# File 'lib/webhookdb/service_integration.rb', line 91 def replicator return Webhookdb::Replicator.create(self) end |
#requires_sequence? ⇒ Boolean
249 250 251 |
# File 'lib/webhookdb/service_integration.rb', line 249 def requires_sequence? return self.replicator.requires_sequence? end |
#sequence_name ⇒ Object
253 254 255 |
# File 'lib/webhookdb/service_integration.rb', line 253 def sequence_name return "replicator_seq_org_#{self.organization_id}_#{self.service_name}_#{self.id}_seq" end |
#sequence_nextval ⇒ Object
267 268 269 |
# File 'lib/webhookdb/service_integration.rb', line 267 def sequence_nextval return self.db.select(Sequel.function(:nextval, self.sequence_name)).single_value end |
#stats ⇒ Webhookdb::ServiceIntegration::Stats
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/webhookdb/service_integration.rb', line 182 def stats all_logged_webhooks = Webhookdb::LoggedWebhook.where( service_integration_opaque_id: self.opaque_id, ).where { inserted_at > 7.days.ago } if all_logged_webhooks.empty? return Stats.new( "We have no record of receiving webhooks for that integration in the past seven days.", {}, ) end # rubocop:disable Naming/VariableNumber count_last_7_days = all_logged_webhooks.count rejected_last_7_days = all_logged_webhooks.where { response_status >= 400 }.count success_last_7_days = (count_last_7_days - rejected_last_7_days) rejected_last_7_days_percent = (rejected_last_7_days.to_f / count_last_7_days) success_last_7_days_percent = (success_last_7_days.to_f / count_last_7_days) last_10 = Webhookdb::LoggedWebhook.order_by(Sequel.desc(:inserted_at)).limit(10).select_map(:response_status) last_10_success, last_10_rejected = last_10.partition { |rs| rs < 400 } data = { count_last_7_days:, count_last_7_days_formatted: count_last_7_days.to_s, success_last_7_days:, success_last_7_days_formatted: success_last_7_days.to_s, success_last_7_days_percent:, success_last_7_days_percent_formatted: "%.1f%%" % (success_last_7_days_percent * 100), rejected_last_7_days:, rejected_last_7_days_formatted: rejected_last_7_days.to_s, rejected_last_7_days_percent:, rejected_last_7_days_percent_formatted: "%.1f%%" % (rejected_last_7_days_percent * 100), successful_of_last_10: last_10_success.size, successful_of_last_10_formatted: last_10_success.size.to_s, rejected_of_last_10: last_10_rejected.size, rejected_of_last_10_formatted: last_10_rejected.size.to_s, } # rubocop:enable Naming/VariableNumber return Stats.new("", data) end |
#unauthed_webhook_endpoint ⇒ Object
112 113 114 |
# File 'lib/webhookdb/service_integration.rb', line 112 def unauthed_webhook_endpoint return Webhookdb.api_url + self.unauthed_webhook_path end |
#unauthed_webhook_path ⇒ Object
108 109 110 |
# File 'lib/webhookdb/service_integration.rb', line 108 def unauthed_webhook_path return "/v1/service_integrations/#{self.opaque_id}" end |