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 302
|
#backfill_key ⇒ String
Returns Key for backfilling.
|
# File 'lib/webhookdb/service_integration.rb', line 305
|
#backfill_secret ⇒ String
Returns Password/secret for backfilling.
|
# File 'lib/webhookdb/service_integration.rb', line 308
|
#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 324
|
#opaque_id ⇒ String
|
# File 'lib/webhookdb/service_integration.rb', line 299
|
#service_name ⇒ String
Returns Lookup name of the service.
|
# File 'lib/webhookdb/service_integration.rb', line 296
|
#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 329
|
#table_name ⇒ String
Returns Name of the table.
|
# File 'lib/webhookdb/service_integration.rb', line 293
|
#webhook_secret ⇒ String
Returns Secret used to sign webhooks.
|
# File 'lib/webhookdb/service_integration.rb', line 311
|
#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 314
|
Class Method Details
.create_disambiguated(service_name, **kwargs) ⇒ Webhookdb::ServiceIntegration
77 78 79 80 |
# File 'lib/webhookdb/service_integration.rb', line 77 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
83 84 85 |
# File 'lib/webhookdb/service_integration.rb', line 83 def self.for_api_key(key) return self.with_encrypted_value(:webhookdb_api_key, key).first end |
Instance Method Details
#authed_api_path ⇒ Object
105 106 107 |
# File 'lib/webhookdb/service_integration.rb', line 105 def authed_api_path return "/v1/organizations/#{self.organization_id}/service_integrations/#{self.opaque_id}" end |
#before_create ⇒ Object
:Sequel Hooks:
286 287 288 |
# File 'lib/webhookdb/service_integration.rb', line 286 def before_create self.ensure_opaque_id end |
#can_be_modified_by?(customer) ⇒ Boolean
87 88 89 |
# File 'lib/webhookdb/service_integration.rb', line 87 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.
135 136 137 138 139 140 |
# File 'lib/webhookdb/service_integration.rb', line 135 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
146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/webhookdb/service_integration.rb', line 146 def destroy_self_and_all_dependents self.dependents.each(&:destroy_self_and_all_dependents) if self.organization.admin_connection_url.present? begin self.replicator.admin_dataset(timeout: :fast) { |ds| ds.db << "DROP TABLE #{self.table_name}" } rescue Sequel::DatabaseError => e raise e unless e.wrapped_exception.is_a?(PG::UndefinedTable) end end self.destroy end |
#ensure_opaque_id ⇒ Object
272 |
# File 'lib/webhookdb/service_integration.rb', line 272 def ensure_opaque_id = self[:opaque_id] ||= self.new_opaque_id |
#ensure_sequence(skip_check: false) ⇒ Object
256 257 258 |
# File 'lib/webhookdb/service_integration.rb', line 256 def ensure_sequence(skip_check: false) self.db << self.ensure_sequence_sql(skip_check:) end |
#ensure_sequence_sql(skip_check: false) ⇒ Object
260 261 262 263 264 |
# File 'lib/webhookdb/service_integration.rb', line 260 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
96 97 98 99 100 101 102 103 |
# File 'lib/webhookdb/service_integration.rb', line 96 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
274 275 276 277 278 279 280 |
# File 'lib/webhookdb/service_integration.rb', line 274 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
270 |
# File 'lib/webhookdb/service_integration.rb', line 270 def new_opaque_id = Webhookdb::Id.new_opaque_id("svi") |
#plan_supports_integration? ⇒ Boolean
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/webhookdb/service_integration.rb', line 117 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
142 143 144 |
# File 'lib/webhookdb/service_integration.rb', line 142 def recursive_dependents return self.dependents + self.dependents.flat_map(&:recursive_dependents) end |
#rename_table(to:) ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/webhookdb/service_integration.rb', line 226 def rename_table(to:) Webhookdb::Organization::DatabaseMigration.guard_ongoing!(self.organization) Webhookdb::DBAdapter.validate_identifier!(to, type: "table") 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
92 93 94 |
# File 'lib/webhookdb/service_integration.rb', line 92 def replicator return Webhookdb::Replicator.create(self) end |
#requires_sequence? ⇒ Boolean
248 249 250 |
# File 'lib/webhookdb/service_integration.rb', line 248 def requires_sequence? return self.replicator.requires_sequence? end |
#sequence_name ⇒ Object
252 253 254 |
# File 'lib/webhookdb/service_integration.rb', line 252 def sequence_name return "replicator_seq_org_#{self.organization_id}_#{self.service_name}_#{self.id}_seq" end |
#sequence_nextval ⇒ Object
266 267 268 |
# File 'lib/webhookdb/service_integration.rb', line 266 def sequence_nextval return self.db.select(Sequel.function(:nextval, self.sequence_name)).single_value end |
#stats ⇒ Webhookdb::ServiceIntegration::Stats
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 222 223 224 |
# File 'lib/webhookdb/service_integration.rb', line 185 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
113 114 115 |
# File 'lib/webhookdb/service_integration.rb', line 113 def unauthed_webhook_endpoint return Webhookdb.api_url + self.unauthed_webhook_path end |
#unauthed_webhook_path ⇒ Object
109 110 111 |
# File 'lib/webhookdb/service_integration.rb', line 109 def unauthed_webhook_path return "/v1/service_integrations/#{self.opaque_id}" end |