bullet_train-outgoing_webhooks 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/app/assets/config/{bullet_train_outgoing_webhooks_web_manifest.js → bullet_train_outgoing_webhooks_manifest.js} +0 -0
  4. data/app/controllers/account/webhooks/outgoing/endpoints_controller.rb +4 -3
  5. data/app/jobs/webhooks/outgoing/delivery_job.rb +7 -0
  6. data/app/jobs/webhooks/outgoing/generate_job.rb +7 -0
  7. data/app/models/concerns/webhooks/outgoing/delivery_attempt_support.rb +64 -0
  8. data/app/models/concerns/webhooks/outgoing/delivery_support.rb +66 -0
  9. data/app/models/concerns/webhooks/outgoing/endpoint_support.rb +37 -0
  10. data/app/models/concerns/webhooks/outgoing/event_support.rb +45 -0
  11. data/app/models/concerns/webhooks/outgoing/issuing_model.rb +69 -0
  12. data/app/models/concerns/webhooks/outgoing/team_support.rb +31 -0
  13. data/app/models/concerns/webhooks/outgoing/uri_filtering.rb +149 -0
  14. data/app/models/webhooks/outgoing/delivery.rb +20 -0
  15. data/app/models/webhooks/outgoing/delivery_attempt.rb +20 -0
  16. data/app/models/webhooks/outgoing/endpoint.rb +20 -0
  17. data/app/models/webhooks/outgoing/event.rb +3 -0
  18. data/app/models/webhooks/outgoing/event_type.rb +13 -0
  19. data/app/models/webhooks/outgoing.rb +5 -0
  20. data/app/models/webhooks.rb +5 -0
  21. data/app/views/account/webhooks/outgoing/deliveries/_menu_item.html.erb +2 -2
  22. data/app/views/account/webhooks/outgoing/delivery_attempts/_menu_item.html.erb +2 -2
  23. data/app/views/account/webhooks/outgoing/endpoints/_breadcrumbs.html.erb +1 -1
  24. data/app/views/account/webhooks/outgoing/endpoints/_form.html.erb +2 -2
  25. data/app/views/account/webhooks/outgoing/endpoints/_index.html.erb +3 -3
  26. data/app/views/account/webhooks/outgoing/endpoints/_menu_item.html.erb +2 -2
  27. data/app/views/account/webhooks/outgoing/endpoints/show.html.erb +1 -1
  28. data/config/routes.rb +1 -1
  29. data/db/migrate/20180420013127_create_webhooks_outgoing_endpoints.rb +10 -0
  30. data/db/migrate/20180420013505_create_webhooks_outgoing_endpoint_event_types.rb +10 -0
  31. data/db/migrate/20180420013852_create_webhooks_outgoing_event_types.rb +9 -0
  32. data/db/migrate/20180420014623_create_webhooks_outgoing_events.rb +12 -0
  33. data/db/migrate/20180420021016_create_webhooks_outgoing_deliveries.rb +11 -0
  34. data/db/migrate/20180420022027_add_team_to_webhooks_outgoing_events.rb +5 -0
  35. data/db/migrate/20180420165345_create_webhooks_outgoing_delivery_attempts.rb +11 -0
  36. data/db/migrate/20180420172112_add_error_message_to_webhooks_outgoing_delivery_attempt.rb +6 -0
  37. data/db/migrate/20181012234232_add_name_to_webhooks_outgoing_endpoints.rb +5 -0
  38. data/db/migrate/20181013030208_add_delivered_at_to_webhooks_outgoing_deliveries.rb +5 -0
  39. data/db/migrate/20181013165056_add_uuid_to_webhooks_outgoing_events.rb +5 -0
  40. data/db/migrate/20181013174539_add_payload_to_webhooks_outgoing_events.rb +5 -0
  41. data/db/migrate/20181013192951_add_attempt_number_to_webhooks_outgoing_delivery_attempts.rb +5 -0
  42. data/db/migrate/20210805060451_change_body_to_data_on_webhooks_outgoing_events.rb +5 -0
  43. data/db/migrate/20211126230846_add_event_type_ids_to_webhooks_outgoing_endpoints.rb +5 -0
  44. data/db/migrate/20211126230847_migrate_event_type_ids_on_webhooks_outgoing_endpoints.rb +8 -0
  45. data/db/migrate/20211127013539_remove_event_type_from_webhooks_outgoing_events.rb +5 -0
  46. data/db/migrate/20211127013800_add_new_event_type_id_to_webhooks_outgoing_events.rb +5 -0
  47. data/db/migrate/20211127014001_migrate_event_types_on_webhooks_outgoing_events.rb +10 -0
  48. data/db/migrate/20211127015712_drop_webhooks_outgoing_endpoint_event_types.rb +9 -0
  49. data/db/migrate/20211127015713_drop_webhooks_outgoing_event_types.rb +9 -0
  50. data/lib/bullet_train/outgoing_webhooks/engine.rb +24 -0
  51. data/lib/bullet_train/outgoing_webhooks/version.rb +1 -1
  52. data/lib/bullet_train/outgoing_webhooks.rb +26 -1
  53. metadata +75 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd1762f370294c608f481e2b2b1ad2782d550180860a2932cda6e926b6400e4a
4
- data.tar.gz: 4f8f18c48c13adbc8f1df26e7eb718572db33c3c8e2c9dd9712c77f6c8467e34
3
+ metadata.gz: 067e22f503dd4bf72b809ba6cd0d0e43d3930a50876a2bc5c846bc186700a3ff
4
+ data.tar.gz: fbddcd29981bdb80d81c7a66ed0fb15ef36d971ebeaf7eab50b4e8ae9d54fe99
5
5
  SHA512:
6
- metadata.gz: 0541cf59eb0f505f0bf21ec0cce2a38bb25157f4e42fb568a1b8d9a9baf064b92fe0bb8261503546d0cab15e6a30ac81d447753f96bdc66b2591438117541643
7
- data.tar.gz: 5620e58172c9eecfb6efcf7cde46b38d28d834256bf6e4346b6369d08a23dfa90155e69ae3c3ae466c3aa7ccb85ac6bd1f078e6ec164028ae06b0b18806045b2
6
+ metadata.gz: 20128e3bb542c2e40d0b93d331a8e0e5bec6cca33ec5b6a2ceaf0e2cbbb8fd155150c40cf82cd89aaedcc1242bce7a8cd5a8c571ff0cc2bd22d0348057b0ff04
7
+ data.tar.gz: b4ace453d0be7351e0ce89e9fc451a492c3e8fc11f4b5270c57012494caefba063cd7577f875bb5dccde9a46a9a17a4b188c4393e0926d2999978d9d992163ec
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2022 Andrew Culver
1
+ Copyright 2022 Bullet Train, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,5 +1,6 @@
1
1
  class Account::Webhooks::Outgoing::EndpointsController < Account::ApplicationController
2
- account_load_and_authorize_resource :endpoint, through: :team, through_association: :webhooks_outgoing_endpoints
2
+ account_load_and_authorize_resource :endpoint, through: BulletTrain::OutgoingWebhooks.parent_association, through_association: :webhooks_outgoing_endpoints
3
+ before_action { @parent = instance_variable_get("@#{BulletTrain::OutgoingWebhooks.parent_association}") }
3
4
 
4
5
  # GET /account/teams/:team_id/webhooks/outgoing/endpoints
5
6
  # GET /account/teams/:team_id/webhooks/outgoing/endpoints.json
@@ -26,7 +27,7 @@ class Account::Webhooks::Outgoing::EndpointsController < Account::ApplicationCon
26
27
  def create
27
28
  respond_to do |format|
28
29
  if @endpoint.save
29
- format.html { redirect_to [:account, @team, :webhooks_outgoing_endpoints], notice: I18n.t("webhooks/outgoing/endpoints.notifications.created") }
30
+ format.html { redirect_to [:account, @parent, :webhooks_outgoing_endpoints], notice: I18n.t("webhooks/outgoing/endpoints.notifications.created") }
30
31
  format.json { render :show, status: :created, location: [:account, @endpoint] }
31
32
  else
32
33
  format.html { render :new, status: :unprocessable_entity }
@@ -54,7 +55,7 @@ class Account::Webhooks::Outgoing::EndpointsController < Account::ApplicationCon
54
55
  def destroy
55
56
  @endpoint.destroy
56
57
  respond_to do |format|
57
- format.html { redirect_to [:account, @team, :webhooks_outgoing_endpoints], notice: I18n.t("webhooks/outgoing/endpoints.notifications.destroyed") }
58
+ format.html { redirect_to [:account, @parent, :webhooks_outgoing_endpoints], notice: I18n.t("webhooks/outgoing/endpoints.notifications.destroyed") }
58
59
  format.json { head :no_content }
59
60
  end
60
61
  end
@@ -0,0 +1,7 @@
1
+ class Webhooks::Outgoing::DeliveryJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(delivery)
5
+ delivery.deliver
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Webhooks::Outgoing::GenerateJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(obj, action)
5
+ obj.generate_webhook_perform(action)
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ module Webhooks::Outgoing::DeliveryAttemptSupport
2
+ extend ActiveSupport::Concern
3
+ include Webhooks::Outgoing::UriFiltering
4
+
5
+ SUCCESS_RESPONSE_CODES = [200, 201, 202, 203, 204, 205, 206, 207, 226].freeze
6
+
7
+ included do
8
+ belongs_to :delivery
9
+ has_one :team, through: :delivery unless BulletTrain::OutgoingWebhooks.parent_class_specified?
10
+ scope :successful, -> { where(response_code: SUCCESS_RESPONSE_CODES) }
11
+
12
+ before_create do
13
+ self.attempt_number = delivery.attempt_count + 1
14
+ end
15
+
16
+ validates :response_code, presence: true
17
+ end
18
+
19
+ def still_attempting?
20
+ error_message.nil? && response_code.nil?
21
+ end
22
+
23
+ def successful?
24
+ SUCCESS_RESPONSE_CODES.include?(response_code)
25
+ end
26
+
27
+ def failed?
28
+ !(successful? || still_attempting?)
29
+ end
30
+
31
+ def attempt
32
+ uri = URI.parse(delivery.endpoint_url)
33
+
34
+ unless allowed_uri?(uri)
35
+ self.response_code = 0
36
+ self.error_message = "URI is not allowed: " + uri
37
+ return false
38
+ end
39
+
40
+ http = Net::HTTP.new(resolve_ip_from_authoritative(uri.hostname.downcase), uri.port)
41
+ http.use_ssl = true if uri.scheme == "https"
42
+ request = Net::HTTP::Post.new(uri.path)
43
+ request.add_field("Host", uri.host)
44
+ request.add_field("Content-Type", "application/json")
45
+ request.body = delivery.event.payload.to_json
46
+
47
+ begin
48
+ response = http.request(request)
49
+ self.response_message = response.message
50
+ self.response_code = response.code
51
+ self.response_body = response.body
52
+ rescue => exception
53
+ self.response_code = 0
54
+ self.error_message = exception.message
55
+ end
56
+
57
+ save
58
+ successful?
59
+ end
60
+
61
+ def label_string
62
+ "#{attempt_number.ordinalize} Attempt"
63
+ end
64
+ end
@@ -0,0 +1,66 @@
1
+ module Webhooks::Outgoing::DeliverySupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :endpoint, class_name: "Webhooks::Outgoing::Endpoint"
6
+ belongs_to :event, class_name: "Webhooks::Outgoing::Event"
7
+
8
+ has_one :team, through: :endpoint unless BulletTrain::OutgoingWebhooks.parent_class_specified?
9
+ has_many :delivery_attempts, class_name: "Webhooks::Outgoing::DeliveryAttempt", dependent: :destroy, foreign_key: :delivery_id
10
+ end
11
+
12
+ ATTEMPT_SCHEDULE = {
13
+ 1 => 15.seconds,
14
+ 2 => 1.minute,
15
+ 3 => 5.minutes,
16
+ 4 => 15.minutes,
17
+ 5 => 1.hour,
18
+ }
19
+
20
+ def label_string
21
+ event.short_uuid
22
+ end
23
+
24
+ def next_reattempt_delay
25
+ ATTEMPT_SCHEDULE[attempt_count]
26
+ end
27
+
28
+ def deliver_async
29
+ if still_attempting?
30
+ Webhooks::Outgoing::DeliveryJob.set(wait: next_reattempt_delay).perform_later(self)
31
+ end
32
+ end
33
+
34
+ def deliver
35
+ if delivery_attempts.create.attempt
36
+ touch(:delivered_at)
37
+ else
38
+ deliver_async
39
+ end
40
+ end
41
+
42
+ def attempt_count
43
+ delivery_attempts.count
44
+ end
45
+
46
+ def delivered?
47
+ delivered_at.present?
48
+ end
49
+
50
+ def still_attempting?
51
+ return false if delivered?
52
+ attempt_count < max_attempts
53
+ end
54
+
55
+ def failed?
56
+ !(delivered? || still_attempting?)
57
+ end
58
+
59
+ def name
60
+ event.short_uuid
61
+ end
62
+
63
+ def max_attempts
64
+ ATTEMPT_SCHEDULE.keys.max
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ module Webhooks::Outgoing::EndpointSupport
2
+ extend ActiveSupport::Concern
3
+ include Webhooks::Outgoing::UriFiltering
4
+
5
+ included do
6
+ belongs_to BulletTrain::OutgoingWebhooks.parent_association
7
+
8
+ has_many :deliveries, class_name: "Webhooks::Outgoing::Delivery", dependent: :destroy, foreign_key: :endpoint_id
9
+ has_many :events, -> { distinct }, through: :deliveries
10
+
11
+ scope :listening_for_event_type_id, ->(event_type_id) { where("event_type_ids @> ? OR event_type_ids = '[]'::jsonb", "\"#{event_type_id}\"") }
12
+
13
+ validates :name, presence: true
14
+
15
+ before_validation { url&.strip! }
16
+
17
+ validates :url, presence: true, allowed_uri: true
18
+
19
+ after_initialize do
20
+ self.event_type_ids ||= []
21
+ end
22
+
23
+ after_save :touch_parent
24
+ end
25
+
26
+ def valid_event_types
27
+ Webhooks::Outgoing::EventType.all
28
+ end
29
+
30
+ def event_types
31
+ event_type_ids.map { |id| Webhooks::Outgoing::EventType.find(id) }
32
+ end
33
+
34
+ def touch_parent
35
+ send(BulletTrain::OutgoingWebhooks.parent_association).touch
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ module Webhooks::Outgoing::EventSupport
2
+ extend ActiveSupport::Concern
3
+ include HasUuid
4
+
5
+ included do
6
+ belongs_to BulletTrain::OutgoingWebhooks.parent_association
7
+ belongs_to :event_type, class_name: "Webhooks::Outgoing::EventType"
8
+ belongs_to :subject, polymorphic: true
9
+ has_many :deliveries, dependent: :destroy
10
+
11
+ before_create do
12
+ self.payload = generate_payload
13
+ end
14
+ end
15
+
16
+ def generate_payload
17
+ {
18
+ event_id: uuid,
19
+ event_type: event_type_id,
20
+ subject_id: subject_id,
21
+ subject_type: subject_type,
22
+ data: data
23
+ }
24
+ end
25
+
26
+ def event_type_name
27
+ payload.dig("event_type")
28
+ end
29
+
30
+ def endpoints
31
+ send(BulletTrain::OutgoingWebhooks.parent_association).webhooks_outgoing_endpoints.listening_for_event_type_id(event_type_id)
32
+ end
33
+
34
+ def deliver
35
+ endpoints.each do |endpoint|
36
+ unless endpoint.deliveries.where(event: self).any?
37
+ endpoint.deliveries.create(event: self, endpoint_url: endpoint.url).deliver_async
38
+ end
39
+ end
40
+ end
41
+
42
+ def label_string
43
+ short_uuid
44
+ end
45
+ end
@@ -0,0 +1,69 @@
1
+ module Webhooks::Outgoing::IssuingModel
2
+ extend ActiveSupport::Concern
3
+
4
+ # define relationships.
5
+ included do
6
+ after_commit :generate_created_webhook, on: [:create]
7
+ after_commit :generate_updated_webhook, on: [:update]
8
+ after_commit :generate_deleted_webhook, on: [:destroy]
9
+ has_many :webhooks_outgoing_events, as: :subject, class_name: "Webhooks::Outgoing::Event", dependent: :nullify
10
+ end
11
+
12
+ def skip_generate_webhook?(action)
13
+ false
14
+ end
15
+
16
+ def parent
17
+ return unless respond_to? BulletTrain::OutgoingWebhooks.parent_association
18
+ send(BulletTrain::OutgoingWebhooks.parent_association)
19
+ end
20
+
21
+ def generate_webhook(action, async: true)
22
+ # allow individual models to opt out of generating webhooks
23
+ return if skip_generate_webhook?(action)
24
+
25
+ # we can only generate webhooks for objects that return their their team / parent.
26
+ return unless parent.present?
27
+
28
+ # Try to find an event type definition for this action.
29
+ event_type = Webhooks::Outgoing::EventType.find_by(id: "#{self.class.name.underscore}.#{action}")
30
+
31
+ # If the event type is defined as one that people can be subscribed to,
32
+ # and this object has a parent where an associated outgoing webhooks endpoint could be registered.
33
+ if event_type
34
+ # Only generate an event record if an endpoint is actually listening for this event type.
35
+ if parent.endpoints_listening_for_event_type?(event_type)
36
+ if async
37
+ # serialization can be heavy so run it as a job
38
+ Webhooks::Outgoing::GenerateJob.perform_later(self, action)
39
+ else
40
+ generate_webhook_perform(action)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def generate_webhook_perform(action)
47
+ event_type = Webhooks::Outgoing::EventType.find_by(id: "#{self.class.name.underscore}.#{action}")
48
+ # TODO This is crazy that we generate JSON just to parse it. Can't be good for performance.
49
+ # Does Jbuilder support generating a hash instead of a JSON string?
50
+ data = JSON.parse(to_api_json)
51
+ webhook = send(BulletTrain::OutgoingWebhooks.parent_association).webhooks_outgoing_events.create(event_type_id: event_type.id, subject: self, data: data)
52
+ webhook.deliver
53
+ end
54
+
55
+ def generate_created_webhook
56
+ generate_webhook(:created)
57
+ end
58
+
59
+ def generate_updated_webhook
60
+ generate_webhook(:updated)
61
+ end
62
+
63
+ def generate_deleted_webhook
64
+ return false unless parent.present?
65
+ return false if parent.being_destroyed?
66
+
67
+ generate_webhook(:deleted, async: false)
68
+ end
69
+ end
@@ -0,0 +1,31 @@
1
+ module Webhooks::Outgoing::TeamSupport
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :webhooks_outgoing_endpoints, class_name: "Webhooks::Outgoing::Endpoint", dependent: :destroy
6
+ has_many :webhooks_outgoing_events, class_name: "Webhooks::Outgoing::Event", dependent: :destroy
7
+
8
+ before_destroy :mark_for_destruction, prepend: true
9
+ end
10
+
11
+ def should_cache_endpoints_listening_for_event_type?
12
+ true
13
+ end
14
+
15
+ def endpoints_listening_for_event_type?(event_type)
16
+ if should_cache_endpoints_listening_for_event_type?
17
+ key = "#{cache_key_with_version}/endpoints_for_event_type/#{event_type.cache_key}"
18
+
19
+ Rails.cache.fetch(key, expires_in: 24.hours, race_condition_ttl: 5.seconds) do
20
+ webhooks_outgoing_endpoints.listening_for_event_type_id(event_type.id).any?
21
+ end
22
+ else
23
+ webhooks_outgoing_endpoints.listening_for_event_type_id(event_type.id).any?
24
+ end
25
+ end
26
+
27
+ def mark_for_destruction
28
+ # This allows downstream logic to check whether a team is being destroyed in order to bypass webhook issuance.
29
+ update_column(:being_destroyed, true)
30
+ end
31
+ end
@@ -0,0 +1,149 @@
1
+ require "resolv"
2
+ require "public_suffix"
3
+
4
+ module Webhooks::Outgoing::UriFiltering
5
+ extend ActiveSupport::Concern
6
+
7
+ # WEBHOOK SECURITY PRIMER
8
+ # =============================================================================
9
+ # Outgoing webhooks can be dangerous. By allowing your users to set
10
+ # up outgoing webhooks, you"re giving them permission to call arbitrary
11
+ # URLs from your server, including URLs that could represent resources
12
+ # internal to your company. Malicious actors can use this permission to
13
+ # examine your infrastructure, call internal APIs, and generally cause
14
+ # havok.
15
+
16
+ # This module attempts to block malicious actors with the following algorithm
17
+ # 1. Block anything but http and https requests
18
+ # 2. Block or allow defined hostnames, both regex and strings
19
+ # 3. Block if `custom_block_callback` returns true (args: self, uri)
20
+ # 4. Allow if `custom_allow_callback` returns true (args: self, uri)
21
+ # 5. Resolve the IP associated with the webhook"s host directly from
22
+ # the authoritative name server for the host"s domain. This IP
23
+ # is cached for the returned DNS TTL
24
+ # 6. Match the given IP against lists of allowed and blocked cidr ranges.
25
+ # The blocked list by default includes all of the defined private address
26
+ # ranges, localhost, the private IPv6 prefix, and the AWS metadata
27
+ # API endpoint.
28
+
29
+ # If at any point a URI is determined to be blocked we call `audit_callback`
30
+ # (args: self, uri) so it can be logged for auditing.
31
+
32
+ # We resolve the IP from the authoritative name server directly so we can avoid
33
+ # certain classes of DNS poisoning attacks.
34
+
35
+ # Users of this gem are _strongly_ enouraged to add additional cidr ranges
36
+ # and hostnames to the appropriate lists and/or implement `custom_block_callback`.
37
+ # At the very least you should add the public hostname that your
38
+ # application uses to the blocked_hostnames list.
39
+
40
+ class AllowedUriValidator < ActiveModel::EachValidator
41
+ def validate_each(record, attribute, value)
42
+ uri = URI.parse(value)
43
+ unless record.allowed_uri?(uri)
44
+ record.errors.add attribute, "is not an allowed uri"
45
+ end
46
+ end
47
+ end
48
+
49
+ def resolve_ip_from_authoritative(hostname)
50
+ begin
51
+ ip = IPAddr.new(hostname)
52
+ return ip.to_s
53
+ rescue IPAddr::InvalidAddressError
54
+ # this is fine, proceed with resolver path
55
+ end
56
+
57
+ cache_key = "#{cache_key_with_version}/uri_ip/#{Digest::SHA2.hexdigest(hostname)}"
58
+
59
+ cached = Rails.cache.read(cache_key)
60
+ if cached
61
+ return cached == "invalid" ? nil : cached
62
+ end
63
+
64
+ begin
65
+ # This is sort of a half-recursive DNS resolver.
66
+ # We can't implement a full recursive resolver using just Resolv::DNS so instead
67
+ # this asks a public cache for the NS record for the given domain. Then it asks
68
+ # the authoritative nameserver directly for the address and caches it according
69
+ # to the returned TTL.
70
+
71
+ config = Rails.configuration.outgoing_webhooks
72
+ ns_resolver = Resolv::DNS.new(nameserver: config[:public_resolvers])
73
+ ns_resolver.timeouts = 1
74
+
75
+ domain = PublicSuffix.domain(hostname)
76
+ authoritative = ns_resolver.getresource(domain, Resolv::DNS::Resource::IN::NS)
77
+
78
+ authoritative_resolver = Resolv::DNS.new(nameserver: [authoritative.name.to_s])
79
+ authoritative_resolver.timeouts = 1
80
+
81
+ resource = authoritative_resolver.getresource(hostname, Resolv::DNS::Resource::IN::A)
82
+ Rails.cache.write(cache_key, resource.address.to_s, expires_in: resource.ttl, race_condition_ttl: 5)
83
+ resource.address.to_s
84
+ rescue IPAddr::InvalidAddressError, ArgumentError # standard:disable Lint/ShadowedException
85
+ Rails.cache.write(cache_key, "invalid", expires_in: 10.minutes, race_condition_ttl: 5)
86
+ nil
87
+ end
88
+ end
89
+
90
+ def allowed_uri?(uri)
91
+ unless _allowed_uri?(uri)
92
+ config = Rails.configuration.outgoing_webhooks
93
+ if config[:audit_callback].present?
94
+ config[:audit_callback].call(self, uri)
95
+ end
96
+ return false
97
+ end
98
+
99
+ true
100
+ end
101
+
102
+ def _allowed_uri?(uri)
103
+ config = Rails.configuration.outgoing_webhooks
104
+ hostname = uri.hostname.downcase
105
+
106
+ return false unless config[:allowed_schemes].include?(uri.scheme)
107
+
108
+ config[:blocked_hostnames].each do |blocked|
109
+ if blocked.is_a?(Regexp)
110
+ return false if blocked.match?(hostname)
111
+ end
112
+
113
+ return false if blocked == hostname
114
+ end
115
+
116
+ config[:allowed_hostnames].each do |allowed|
117
+ if allowed.is_a?(Regexp)
118
+ return true if allowed.match?(hostname)
119
+ end
120
+
121
+ return true if allowed == hostname
122
+ end
123
+
124
+ if config[:custom_allow_callback].present?
125
+ return true if config[:custom_allow_callback].call(self, uri)
126
+ end
127
+
128
+ if config[:custom_block_callback].present?
129
+ return false if config[:custom_block_callback].call(self, uri)
130
+ end
131
+
132
+ resolved_ip = resolve_ip_from_authoritative(hostname)
133
+ return false if resolved_ip.nil?
134
+
135
+ begin
136
+ config[:allowed_cidrs].each do |cidr|
137
+ return true if IPAddr.new(cidr).include?(resolved_ip)
138
+ end
139
+
140
+ config[:blocked_cidrs].each do |cidr|
141
+ return false if IPAddr.new(cidr).include?(resolved_ip)
142
+ end
143
+ rescue IPAddr::InvalidAddressError
144
+ return false
145
+ end
146
+
147
+ true
148
+ end
149
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::Delivery < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::DeliverySupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::DeliveryAttempt < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::DeliveryAttemptSupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,20 @@
1
+ class Webhooks::Outgoing::Endpoint < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::EndpointSupport
3
+ # 🚅 add concerns above.
4
+
5
+ # 🚅 add belongs_to associations above.
6
+
7
+ # 🚅 add has_many associations above.
8
+
9
+ # 🚅 add has_one associations above.
10
+
11
+ # 🚅 add scopes above.
12
+
13
+ # 🚅 add validations above.
14
+
15
+ # 🚅 add callbacks above.
16
+
17
+ # 🚅 add delegations above.
18
+
19
+ # 🚅 add methods above.
20
+ end
@@ -0,0 +1,3 @@
1
+ class Webhooks::Outgoing::Event < BulletTrain::OutgoingWebhooks.base_class.constantize
2
+ include Webhooks::Outgoing::EventSupport
3
+ end
@@ -0,0 +1,13 @@
1
+ class Webhooks::Outgoing::EventType < ApplicationHash
2
+ self.data = YAML.load_file("config/models/webhooks/outgoing/event_types.yml").map do |topic, events|
3
+ events.map { |event| event == "crud" ? ["created", "updated", "deleted"] : event }.flatten.map { |event| {id: "#{topic}.#{event}"} }
4
+ end.flatten
5
+
6
+ def label_string
7
+ name
8
+ end
9
+
10
+ def name
11
+ id
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Webhooks::Outgoing
2
+ def self.table_name_prefix
3
+ "webhooks_outgoing_"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Webhooks
2
+ def self.table_name_prefix
3
+ "webhooks_"
4
+ end
5
+ end
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::Delivery.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::Delivery.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_deliveries]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_deliveries]),
4
4
  label: t('webhooks/outgoing/deliveries.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::DeliveryAttempt.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::DeliveryAttempt.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_delivery_attempts]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_delivery_attempts]),
4
4
  label: t('webhooks/outgoing/delivery_attempts.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -1,5 +1,5 @@
1
1
  <% endpoint ||= @endpoint %>
2
- <% team ||= @team || endpoint&.team %>
2
+ <% team ||= @parent || endpoint&.send(BulletTrain::OutgoingWebhooks.parent_association) %>
3
3
  <%= render 'account/teams/breadcrumbs', team: team %>
4
4
  <%= render 'account/shared/breadcrumb', label: t('.label'), url: [:account, team, :webhooks_outgoing_endpoints] %>
5
5
  <% if endpoint&.persisted? %>
@@ -1,4 +1,4 @@
1
- <%= form_with model: endpoint, url: (endpoint.persisted? ? [:account, endpoint] : [:account, @team, :webhooks_outgoing_endpoints]), local: true, class: 'form' do |form| %>
1
+ <%= form_with model: endpoint, url: (endpoint.persisted? ? [:account, endpoint] : [:account, @parent, :webhooks_outgoing_endpoints]), local: true, class: 'form' do |form| %>
2
2
  <%= render 'account/shared/forms/errors', form: form %>
3
3
 
4
4
  <% with_field_settings form: form do %>
@@ -14,7 +14,7 @@
14
14
  <% if form.object.persisted? %>
15
15
  <%= link_to t('global.buttons.cancel'), [:account, endpoint], class: "button-secondary" %>
16
16
  <% else %>
17
- <%= link_to t('global.buttons.cancel'), [:account, @team, :webhooks_outgoing_endpoints], class: "button-secondary" %>
17
+ <%= link_to t('global.buttons.cancel'), [:account, @parent, :webhooks_outgoing_endpoints], class: "button-secondary" %>
18
18
  <% end %>
19
19
  </div>
20
20
 
@@ -1,4 +1,4 @@
1
- <% team = @team || @team %>
1
+ <% team = @parent %>
2
2
  <% context ||= team %>
3
3
  <% collection ||= :webhooks_outgoing_endpoints %>
4
4
  <% hide_actions ||= false %>
@@ -49,8 +49,8 @@
49
49
  <% p.content_for :actions do %>
50
50
  <% unless hide_actions %>
51
51
  <% if context == team %>
52
- <% if can? :create, Webhooks::Outgoing::Endpoint.new(team: team) %>
53
- <%= link_to t('.buttons.new'), [:new, :account, team, :webhooks_outgoing_endpoint], class: "#{first_button_primary(:webhooks_outgoing_endpoint)} new" %>
52
+ <% if can? :create, Webhooks::Outgoing::Endpoint.new(BulletTrain::OutgoingWebhooks.parent_association => @parent) %>
53
+ <%= link_to t('.buttons.new'), [:new, :account, @parent, :webhooks_outgoing_endpoint], class: "#{first_button_primary(:webhooks_outgoing_endpoint)} new" %>
54
54
  <% end %>
55
55
  <% end %>
56
56
 
@@ -1,6 +1,6 @@
1
- <% if can? :read, Webhooks::Outgoing::Endpoint.new(team: current_team) %>
1
+ <% if can? :read, Webhooks::Outgoing::Endpoint.new(BulletTrain::OutgoingWebhooks.parent_association => send(BulletTrain::OutgoingWebhooks.current_parent_method)) %>
2
2
  <%= render 'account/shared/menu/item', {
3
- url: main_app.polymorphic_path([:account, current_team, :webhooks_outgoing_endpoints]),
3
+ url: main_app.polymorphic_path([:account, send(BulletTrain::OutgoingWebhooks.current_parent_method), :webhooks_outgoing_endpoints]),
4
4
  label: t('webhooks/outgoing/endpoints.navigation.label'),
5
5
  } do |p| %>
6
6
  <% p.content_for :icon do %>
@@ -32,7 +32,7 @@
32
32
  <% p.content_for :actions do %>
33
33
  <%= link_to t('.buttons.edit'), [:edit, :account, @endpoint], class: first_button_primary if can? :edit, @endpoint %>
34
34
  <%= button_to t('.buttons.destroy'), [:account, @endpoint], method: :delete, class: first_button_primary, data: { confirm: t('.buttons.confirmations.destroy', model_locales(@endpoint)) } if can? :destroy, @endpoint %>
35
- <%= link_to t('global.buttons.back'), [:account, @team, :webhooks_outgoing_endpoints], class: first_button_primary %>
35
+ <%= link_to t('global.buttons.back'), [:account, @parent, :webhooks_outgoing_endpoints], class: first_button_primary %>
36
36
  <% end %>
37
37
  <% end %>
38
38
 
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  Rails.application.routes.draw do
2
2
  namespace :account do
3
3
  shallow do
4
- resources :teams do
4
+ resources BulletTrain::OutgoingWebhooks.parent_resource do
5
5
  namespace :webhooks do
6
6
  namespace :outgoing do
7
7
  resources :events
@@ -0,0 +1,10 @@
1
+ class CreateWebhooksOutgoingEndpoints < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_endpoints do |t|
4
+ t.references :team, foreign_key: true
5
+ t.text :url
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateWebhooksOutgoingEndpointEventTypes < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_endpoint_event_types do |t|
4
+ t.integer :endpoint_id
5
+ t.integer :event_type_id
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateWebhooksOutgoingEventTypes < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_event_types do |t|
4
+ t.string :name
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ class CreateWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_events do |t|
4
+ t.integer :event_type_id
5
+ t.integer :subject_id
6
+ t.string :subject_type
7
+ t.jsonb :body
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class CreateWebhooksOutgoingDeliveries < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_deliveries do |t|
4
+ t.integer :endpoint_id
5
+ t.integer :event_id
6
+ t.text :endpoint_url
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddTeamToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_reference :webhooks_outgoing_events, :team, foreign_key: true
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ class CreateWebhooksOutgoingDeliveryAttempts < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :webhooks_outgoing_delivery_attempts do |t|
4
+ t.integer :delivery_id
5
+ t.integer :response_code
6
+ t.text :response_body
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class AddErrorMessageToWebhooksOutgoingDeliveryAttempt < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_delivery_attempts, :response_message, :text
4
+ add_column :webhooks_outgoing_delivery_attempts, :error_message, :text
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddNameToWebhooksOutgoingEndpoints < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_endpoints, :name, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddDeliveredAtToWebhooksOutgoingDeliveries < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_deliveries, :delivered_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddUuidToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :uuid, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddPayloadToWebhooksOutgoingEvents < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :payload, :jsonb
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddAttemptNumberToWebhooksOutgoingDeliveryAttempts < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :webhooks_outgoing_delivery_attempts, :attempt_number, :integer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ChangeBodyToDataOnWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ rename_column :webhooks_outgoing_events, :body, :data
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddEventTypeIdsToWebhooksOutgoingEndpoints < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :webhooks_outgoing_endpoints, :event_type_ids, :jsonb, default: []
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class MigrateEventTypeIdsOnWebhooksOutgoingEndpoints < ActiveRecord::Migration[6.1]
2
+ def change
3
+ Webhooks::Outgoing::Endpoint.find_each do |endpoint|
4
+ event_type_ids = ActiveRecord::Base.connection.execute("SELECT * FROM webhooks_outgoing_endpoint_event_types JOIN webhooks_outgoing_event_types ON webhooks_outgoing_endpoint_event_types.event_type_id = webhooks_outgoing_event_types.id WHERE endpoint_id = #{endpoint.id}").to_a.map { |result| result.dig("name") }
5
+ endpoint.update(event_type_ids: event_type_ids)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveEventTypeFromWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ remove_reference :webhooks_outgoing_events, :event_type, null: false, foreign_key: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddNewEventTypeIdToWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :webhooks_outgoing_events, :event_type_id, :string
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class MigrateEventTypesOnWebhooksOutgoingEvents < ActiveRecord::Migration[6.1]
2
+ def up
3
+ Webhooks::Outgoing::Event.find_each do |event|
4
+ event.update_column(:event_type_id, event.payload.dig("event_type"))
5
+ end
6
+ end
7
+
8
+ def down
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class DropWebhooksOutgoingEndpointEventTypes < ActiveRecord::Migration[6.1]
2
+ def up
3
+ drop_table :webhooks_outgoing_endpoint_event_types
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class DropWebhooksOutgoingEventTypes < ActiveRecord::Migration[6.1]
2
+ def up
3
+ drop_table :webhooks_outgoing_event_types
4
+ end
5
+
6
+ def down
7
+ raise ActiveRecord::IrreversibleMigration
8
+ end
9
+ end
@@ -1,6 +1,30 @@
1
1
  module BulletTrain
2
2
  module OutgoingWebhooks
3
3
  class Engine < ::Rails::Engine
4
+ config.before_configuration do
5
+ default_blocked_cidrs = %w[
6
+ 10.0.0.0/8
7
+ 172.16.0.0/12
8
+ 192.168.0.0/16
9
+ 100.64.0.0/10
10
+ 127.0.0.0/8
11
+ 169.254.169.254/32
12
+ fc00::/7
13
+ ::1
14
+ ]
15
+
16
+ config.outgoing_webhooks = {
17
+ blocked_cidrs: default_blocked_cidrs,
18
+ allowed_cidrs: [],
19
+ blocked_hostnames: %w[localhost],
20
+ allowed_hostnames: [],
21
+ public_resolvers: %w[8.8.8.8 1.1.1.1],
22
+ allowed_schemes: %w[http https],
23
+ custom_block_callback: nil,
24
+ custom_allow_callback: nil,
25
+ audit_callback: ->(obj, uri) { Rails.logger.error("BlockedURI obj=#{obj.persisted? ? obj.to_global_id : "New #{obj.class}"} uri=#{uri}") }
26
+ }
27
+ end
4
28
  end
5
29
  end
6
30
  end
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module OutgoingWebhooks
3
- VERSION = "1.0.3"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -3,6 +3,31 @@ require "bullet_train/outgoing_webhooks/engine"
3
3
 
4
4
  module BulletTrain
5
5
  module OutgoingWebhooks
6
- # Your code goes here...
6
+ def self.default_for(klass, method, default_value)
7
+ klass.respond_to?(method) ? klass.send(method) || default_value : default_value
8
+ end
9
+
10
+ mattr_accessor :parent_class, default: default_for(BulletTrain, :parent_class, "Team")
11
+ mattr_accessor :base_class, default: default_for(BulletTrain, :base_class, "ApplicationRecord")
12
+
13
+ def self.parent_association
14
+ parent_class.underscore.to_sym
15
+ end
16
+
17
+ def self.parent_resource
18
+ parent_class.underscore.pluralize.to_sym
19
+ end
20
+
21
+ def self.parent_class_specified?
22
+ parent_class != "Team"
23
+ end
24
+
25
+ def self.current_parent_method
26
+ "current_#{parent_association}"
27
+ end
28
+
29
+ def self.parent_association_id
30
+ "#{parent_association}_id".to_sym
31
+ end
7
32
  end
8
33
  end
metadata CHANGED
@@ -1,30 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train-outgoing_webhooks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-23 00:00:00.000000000 Z
11
+ date: 2022-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: standard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rails
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - ">="
18
32
  - !ruby/object:Gem::Version
19
- version: 7.0.0
33
+ version: 6.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 6.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: public_suffix
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
20
48
  type: :runtime
21
49
  prerelease: false
22
50
  version_requirements: !ruby/object:Gem::Requirement
23
51
  requirements:
24
52
  - - ">="
25
53
  - !ruby/object:Gem::Version
26
- version: 7.0.0
27
- description: Bullet Train Outgoing Webhooks
54
+ version: '0'
55
+ description: Allow users of your Rails application to subscribe and receive webhooks
56
+ when activity takes place in your application.
28
57
  email:
29
58
  - andrew.culver@gmail.com
30
59
  executables: []
@@ -34,10 +63,26 @@ files:
34
63
  - MIT-LICENSE
35
64
  - README.md
36
65
  - Rakefile
37
- - app/assets/config/bullet_train_outgoing_webhooks_web_manifest.js
66
+ - app/assets/config/bullet_train_outgoing_webhooks_manifest.js
38
67
  - app/controllers/account/webhooks/outgoing/deliveries_controller.rb
39
68
  - app/controllers/account/webhooks/outgoing/delivery_attempts_controller.rb
40
69
  - app/controllers/account/webhooks/outgoing/endpoints_controller.rb
70
+ - app/jobs/webhooks/outgoing/delivery_job.rb
71
+ - app/jobs/webhooks/outgoing/generate_job.rb
72
+ - app/models/concerns/webhooks/outgoing/delivery_attempt_support.rb
73
+ - app/models/concerns/webhooks/outgoing/delivery_support.rb
74
+ - app/models/concerns/webhooks/outgoing/endpoint_support.rb
75
+ - app/models/concerns/webhooks/outgoing/event_support.rb
76
+ - app/models/concerns/webhooks/outgoing/issuing_model.rb
77
+ - app/models/concerns/webhooks/outgoing/team_support.rb
78
+ - app/models/concerns/webhooks/outgoing/uri_filtering.rb
79
+ - app/models/webhooks.rb
80
+ - app/models/webhooks/outgoing.rb
81
+ - app/models/webhooks/outgoing/delivery.rb
82
+ - app/models/webhooks/outgoing/delivery_attempt.rb
83
+ - app/models/webhooks/outgoing/endpoint.rb
84
+ - app/models/webhooks/outgoing/event.rb
85
+ - app/models/webhooks/outgoing/event_type.rb
41
86
  - app/views/account/webhooks/outgoing/deliveries/_breadcrumbs.html.erb
42
87
  - app/views/account/webhooks/outgoing/deliveries/_delivery.json.jbuilder
43
88
  - app/views/account/webhooks/outgoing/deliveries/_form.html.erb
@@ -78,6 +123,27 @@ files:
78
123
  - config/locales/en/webhooks/outgoing/endpoints.en.yml
79
124
  - config/locales/en/webhooks/outgoing/events.en.yml
80
125
  - config/routes.rb
126
+ - db/migrate/20180420013127_create_webhooks_outgoing_endpoints.rb
127
+ - db/migrate/20180420013505_create_webhooks_outgoing_endpoint_event_types.rb
128
+ - db/migrate/20180420013852_create_webhooks_outgoing_event_types.rb
129
+ - db/migrate/20180420014623_create_webhooks_outgoing_events.rb
130
+ - db/migrate/20180420021016_create_webhooks_outgoing_deliveries.rb
131
+ - db/migrate/20180420022027_add_team_to_webhooks_outgoing_events.rb
132
+ - db/migrate/20180420165345_create_webhooks_outgoing_delivery_attempts.rb
133
+ - db/migrate/20180420172112_add_error_message_to_webhooks_outgoing_delivery_attempt.rb
134
+ - db/migrate/20181012234232_add_name_to_webhooks_outgoing_endpoints.rb
135
+ - db/migrate/20181013030208_add_delivered_at_to_webhooks_outgoing_deliveries.rb
136
+ - db/migrate/20181013165056_add_uuid_to_webhooks_outgoing_events.rb
137
+ - db/migrate/20181013174539_add_payload_to_webhooks_outgoing_events.rb
138
+ - db/migrate/20181013192951_add_attempt_number_to_webhooks_outgoing_delivery_attempts.rb
139
+ - db/migrate/20210805060451_change_body_to_data_on_webhooks_outgoing_events.rb
140
+ - db/migrate/20211126230846_add_event_type_ids_to_webhooks_outgoing_endpoints.rb
141
+ - db/migrate/20211126230847_migrate_event_type_ids_on_webhooks_outgoing_endpoints.rb
142
+ - db/migrate/20211127013539_remove_event_type_from_webhooks_outgoing_events.rb
143
+ - db/migrate/20211127013800_add_new_event_type_id_to_webhooks_outgoing_events.rb
144
+ - db/migrate/20211127014001_migrate_event_types_on_webhooks_outgoing_events.rb
145
+ - db/migrate/20211127015712_drop_webhooks_outgoing_endpoint_event_types.rb
146
+ - db/migrate/20211127015713_drop_webhooks_outgoing_event_types.rb
81
147
  - lib/bullet_train/outgoing_webhooks.rb
82
148
  - lib/bullet_train/outgoing_webhooks/engine.rb
83
149
  - lib/bullet_train/outgoing_webhooks/version.rb
@@ -103,8 +169,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
169
  - !ruby/object:Gem::Version
104
170
  version: '0'
105
171
  requirements: []
106
- rubygems_version: 3.2.22
172
+ rubygems_version: 3.3.7
107
173
  signing_key:
108
174
  specification_version: 4
109
- summary: Bullet Train Outgoing Webhooks
175
+ summary: Allow users of your Rails application to subscribe and receive webhooks when
176
+ activity takes place in your application.
110
177
  test_files: []