Module: Webhookdb

Extended by:
MethodUtilities
Includes:
Appydays::Configurable, Appydays::Loggable
Defined in:
lib/webhookdb.rb,
lib/webhookdb/json.rb,
lib/webhookdb/version.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

APPLICATION_NAME =
"Webhookdb"
RACK_ENV =
ENV.fetch("RACK_ENV", "development")
COMMIT =
ENV.fetch("HEROKU_SLUG_COMMIT", "unknown-commit")
RELEASE =
ENV.fetch("HEROKU_RELEASE_VERSION", "unknown-release")
RELEASE_CREATED_AT =
ENV.fetch("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
VERSION =
"1.0.0"

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.



110
111
112
113
114
115
116
117
118
# File 'lib/webhookdb.rb', line 110

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.



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

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



90
91
92
93
94
95
96
97
98
# File 'lib/webhookdb.rb', line 90

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)


81
82
83
# File 'lib/webhookdb.rb', line 81

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.



197
198
199
# File 'lib/webhookdb.rb', line 197

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.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/webhookdb.rb', line 202

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



150
151
152
# File 'lib/webhookdb.rb', line 150

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)


160
161
162
163
164
165
166
# File 'lib/webhookdb.rb', line 160

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