Class: Webhookdb::ServiceIntegration

Inherits:
Object
  • Object
show all
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. Blank string returns all info.

["id", "service", "table", "url", "webhook_secret", ""].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#api_urlString

Returns Root Url of the api to backfill from.

Returns:

  • (String)

    Root Url of the api to backfill from



# File 'lib/webhookdb/service_integration.rb', line 277

#backfill_keyString

Returns Key for backfilling.

Returns:

  • (String)

    Key for backfilling.



# File 'lib/webhookdb/service_integration.rb', line 280

#backfill_secretString

Returns Password/secret for backfilling.

Returns:

  • (String)

    Password/secret for backfilling.



# File 'lib/webhookdb/service_integration.rb', line 283

#data_encryption_secretString

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.

Returns:

  • (String)

    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 292

#depends_onWebhookdb::ServiceIntegration



# File 'lib/webhookdb/service_integration.rb', line 289

#opaque_idString

Returns:

  • (String)


# File 'lib/webhookdb/service_integration.rb', line 274

#organizationWebhookdb::Organization



# File 'lib/webhookdb/service_integration.rb', line 265

#service_nameString

Returns Lookup name of the service.

Returns:

  • (String)

    Lookup name of the service



# File 'lib/webhookdb/service_integration.rb', line 271

#skip_webhook_verificationBoolean

Returns Set this to disable webhook verification on this integration. Useful when replaying logged webhooks.

Returns:

  • (Boolean)

    Set this to disable webhook verification on this integration. Useful when replaying logged webhooks.



# File 'lib/webhookdb/service_integration.rb', line 297

#table_nameString

Returns Name of the table.

Returns:

  • (String)

    Name of the table



# File 'lib/webhookdb/service_integration.rb', line 268

#webhook_secretString

Returns Secret used to sign webhooks.

Returns:

  • (String)

    Secret used to sign webhooks.



# File 'lib/webhookdb/service_integration.rb', line 286

Class Method Details

.create_disambiguated(service_name, **kwargs) ⇒ Object



67
68
69
70
# File 'lib/webhookdb/service_integration.rb', line 67

def self.create_disambiguated(service_name, **kwargs)
  kwargs[:table_name] ||= "#{service_name}_#{SecureRandom.hex(2)}"
  return self.create(service_name:, **kwargs)
end

Instance Method Details

#authed_api_pathObject



90
91
92
# File 'lib/webhookdb/service_integration.rb', line 90

def authed_api_path
  return "/v1/organizations/#{self.organization_id}/service_integrations/#{self.opaque_id}"
end

#before_createObject

:Sequel Hooks:



261
262
263
# File 'lib/webhookdb/service_integration.rb', line 261

def before_create
  self[:opaque_id] ||= Webhookdb::Id.new_opaque_id("svi")
end

#can_be_modified_by?(customer) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/webhookdb/service_integration.rb', line 72

def can_be_modified_by?(customer)
  return customer.verified_member_of?(self.organization)
end

#dependency_candidatesArray<Webhookdb::ServiceIntegration>

Return service integrations that can be used as the dependency for this integration.

Returns:



120
121
122
123
124
125
# File 'lib/webhookdb/service_integration.rb', line 120

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_dependentsObject



131
132
133
134
135
136
137
138
139
140
# File 'lib/webhookdb/service_integration.rb', line 131

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_sequence(skip_check: false) ⇒ Object



243
244
245
# File 'lib/webhookdb/service_integration.rb', line 243

def ensure_sequence(skip_check: false)
  self.db << self.ensure_sequence_sql(skip_check:)
end

#ensure_sequence_sql(skip_check: false) ⇒ Object



247
248
249
250
251
# File 'lib/webhookdb/service_integration.rb', line 247

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_tagsObject



81
82
83
84
85
86
87
88
# File 'lib/webhookdb/service_integration.rb', line 81

def log_tags
  return {
    service_integration_id: self.id,
    service_integration_name: self.service_name,
    service_integration_table: self.table_name,
    **self.organization.log_tags,
  }
end

#plan_supports_integration?Boolean

Returns:

  • (Boolean)


102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/webhookdb/service_integration.rb', line 102

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_dependentsObject



127
128
129
# File 'lib/webhookdb/service_integration.rb', line 127

def recursive_dependents
  return self.dependents + self.dependents.flat_map(&:recursive_dependents)
end

#rename_table(to:) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/webhookdb/service_integration.rb', line 209

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

#replicatorWebhookdb::Replicator::Base



77
78
79
# File 'lib/webhookdb/service_integration.rb', line 77

def replicator
  return Webhookdb::Replicator.create(self)
end

#requires_sequence?Boolean

Returns:

  • (Boolean)


235
236
237
# File 'lib/webhookdb/service_integration.rb', line 235

def requires_sequence?
  return self.replicator.requires_sequence?
end

#sequence_nameObject



239
240
241
# File 'lib/webhookdb/service_integration.rb', line 239

def sequence_name
  return "replicator_seq_org_#{self.organization_id}_#{self.service_name}_#{self.id}_seq"
end

#sequence_nextvalObject



253
254
255
# File 'lib/webhookdb/service_integration.rb', line 253

def sequence_nextval
  return self.db.select(Sequel.function(:nextval, self.sequence_name)).single_value
end

#statsWebhookdb::ServiceIntegration::Stats



168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
# File 'lib/webhookdb/service_integration.rb', line 168

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_endpointObject



98
99
100
# File 'lib/webhookdb/service_integration.rb', line 98

def unauthed_webhook_endpoint
  return Webhookdb.api_url + self.unauthed_webhook_path
end

#unauthed_webhook_pathObject



94
95
96
# File 'lib/webhookdb/service_integration.rb', line 94

def unauthed_webhook_path
  return "/v1/service_integrations/#{self.opaque_id}"
end