Module: Webhookdb

Extended by:
MethodUtilities
Includes:
Appydays::Configurable, Appydays::Loggable
Defined in:
lib/webhookdb/json.rb,
lib/webhookdb/version.rb,
lib/webhookdb.rb

Defined Under Namespace

Modules: API, AWS, Admin, AdminAPI, Apps, Async, Console, Crypto, Dbutil, DemoMode, EmailOctopus, Enumerable, Fixtures, Formatting, Front, GoogleCalendar, Http, Icalendar, Id, IntegrationSpecHelpers, Intercom, Jobs, Json, Liquid, Message, Messages, MethodUtilities, MicrosoftCalendar, Nextpax, Oauth, PhoneNumber, Plaid, Platform, Plivo, Postgres, Postmark, Pry, Redis, Sentry, Signalwire, SpecHelpers, Sponsy, Tasks, Theranest, Transistor, Twilio, WindowsTZ Classes: AggregateResult, BackfillJob, Backfiller, Cloudflare, ConnectionCache, Convertkit, Customer, DBAdapter, DatabaseDocument, DatabaseLocked, DeveloperAlert, Github, Heroku, Idempotency, Increase, InvalidInput, InvalidPostcondition, InvalidPrecondition, InvariantViolation, LockFailed, LoggedWebhook, Organization, OrganizationMembership, RegressionModeSkip, Replicator, Role, Service, ServiceIntegration, Shopify, Slack, Snowflake, Stripe, Subscription, SyncTarget, TypedStruct, WebhookResponse, WebhookSubscription, Webterm, Xml

Constant Summary collapse

VERSION =
"1.1.0"
APPLICATION_NAME =
"Webhookdb"
RACK_ENV =
Appydays::Configurable.fetch_env(["RACK_ENV", "RUBY_ENV"], "development")
COMMIT =
Appydays::Configurable.fetch_env(["COMMIT", "GIT_SHA", "HEROKU_SLUG_COMMIT"], "00000000")
RELEASE =
Appydays::Configurable.fetch_env(["RELEASE", "GIT_REF", "HEROKU_RELEASE_VERSION"], "unknown-release")
RELEASE_CREATED_AT =
Appydays::Configurable.fetch_env(
  ["RELEASE_CREATED_AT", "BUILT_AT", "HEROKU_RELEASE_CREATED_AT"],
  Time.at(0).utc.iso8601,
)
INTEGRATION_TESTS_ENABLED =
ENV.fetch("INTEGRATION_TESTS", false)
DATA_DIR =
Pathname(__FILE__).dirname.parent + "data"
UNAMBIGUOUS_CHARS =

Remove ambiguous characters (L, I, 1 or 0, O) and vowels from possible codes to avoid creating ambiguous codes or real words.

"CDFGHJKMNPQRTVWXYZ23469".chars.freeze
NUMBERS_TO_WORDS =
{
  "0" => "zero",
  "1" => "one",
  "2" => "two",
  "3" => "three",
  "4" => "four",
  "5" => "five",
  "6" => "six",
  "7" => "seven",
  "8" => "eight",
  "9" => "nine",
}.freeze

Class Method Summary collapse

Methods included from MethodUtilities

attr_predicate, attr_predicate_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader

Class Method Details

.cached_get(key) ⇒ Object

If globals caching is enabled, see if there is a cached value under key and return it if so. If there is not, evaluate the given block and store that value. Generally used for looking up well-known database objects like certain roles.



138
139
140
141
142
143
144
145
146
# File 'lib/webhookdb.rb', line 138

def self.cached_get(key)
  if self.use_globals_cache
    result = self.globals_cache[key]
    return result if result
  end
  result = yield()
  self.globals_cache[key] = result
  return result
end

.idempotency_key(instance, *parts) ⇒ Object

Generate a key for the specified Sequel model instance and any additional parts that can be used for idempotent requests.



156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/webhookdb.rb', line 156

def self.idempotency_key(instance, *parts)
  key = "%s-%s" % [instance.class.implicit_table_name, instance.pk]

  if instance.respond_to?(:updated_at) && instance.updated_at
    parts << instance.updated_at
  elsif instance.respond_to?(:created_at) && instance.created_at
    parts << instance.created_at
  end
  parts << SecureRandom.hex(8) if self.bust_idempotency
  key << "-" << parts.map(&:to_s).join("-") unless parts.empty?

  return key
end

.load_appObject



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

def self.load_app
  $stdout.sync = true
  $stderr.sync = true

  Appydays::Loggable.configure_12factor(format: self.log_format, application: APPLICATION_NAME)

  require "webhookdb/postgres"
  Webhookdb::Postgres.load_models
end

.regression_mode?Boolean

Regression mode is true when we re replaying webhooks locally, or for some other reason, want to disable certain checks we use in production. For example, we may want to ignore certain errors (like if integrations are missing dependency rows), or disable certain validations (like always assume the webhook is valid).

Returns:

  • (Boolean)


109
110
111
# File 'lib/webhookdb.rb', line 109

def self.regression_mode?
  return self.regression_mode
end

.request_user_and_adminObject

Return the request user and admin stored in TLS. See service.rb for implementation.

Note that the second return value (the admin) will be nil if not authed as an admin, and if an admin is impersonating, the impersonated customer is the first value.

Both values will be nil if no user is authed or this is called outside of a request.

Usually these fields should only be used where it would be sufficiently difficult to pass the current user through the stack. In the API, you should instead use the ‘current customer’ methods like current_customer, and admin_customer, NOT using TLS. Outside of the API, this should only be used for things like auditing; it should NOT, for example, ever be used to determine the ‘customer owner’ of objects being created. Nearly all code will be simpler if the current customer is passed around. But it would be too complex for some code (like auditing) so this system exists. Overuse of request_user_and_admin will inevitably lead to regret.



225
226
227
# File 'lib/webhookdb.rb', line 225

def self.request_user_and_admin
  return Thread.current[:request_user], Thread.current[:request_admin]
end

.set_request_user_and_admin(user, admin, &block) ⇒ Object

Return the request user stored in TLS. See service.rb for details.



230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/webhookdb.rb', line 230

def self.set_request_user_and_admin(user, admin, &block)
  if !user.nil? && !admin.nil? && self.request_user_and_admin != [nil, nil]
    raise Webhookdb::InvalidPrecondition, "request user is already set: #{user}, #{admin}"
  end
  Thread.current[:request_user] = user
  Thread.current[:request_admin] = admin
  return if block.nil?
  begin
    yield
  ensure
    Thread.current[:request_user] = nil
    Thread.current[:request_admin] = nil
  end
end

.take_unambiguous_chars(n) ⇒ Object



178
179
180
# File 'lib/webhookdb.rb', line 178

def self.take_unambiguous_chars(n)
  return Array.new(n) { UNAMBIGUOUS_CHARS.sample }.join
end

.to_slug(s) ⇒ Object

Convert a string into something we consistently use for slugs: a-z, 0-9, and underscores only. Leading numbers are converted to words.

Acme + Corporation -> “acme_corporation” 1Byte -> “one_byte” 10Byte -> “one0_byte”

Raises:

  • (ArgumentError)


188
189
190
191
192
193
194
# File 'lib/webhookdb.rb', line 188

def self.to_slug(s)
  raise ArgumentError, "s cannot be nil" if s.nil?
  return "" if s.blank?
  slug = s.downcase.strip.gsub(/[^a-z0-9]/, "_").squeeze("_")
  slug = NUMBERS_TO_WORDS[slug.first] + slug[1..] if slug.first.match?(/[0-9]/)
  return slug
end