easyhooks 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +374 -0
- data/lib/easyhooks/action.rb +37 -0
- data/lib/easyhooks/base.rb +22 -0
- data/lib/easyhooks/concerns/helpers.rb +35 -0
- data/lib/easyhooks/concerns/validators.rb +93 -0
- data/lib/easyhooks/hook.rb +8 -0
- data/lib/easyhooks/migration.rb +12 -0
- data/lib/easyhooks/post_processor.rb +101 -0
- data/lib/easyhooks/specification.rb +108 -0
- data/lib/easyhooks/store.rb +51 -0
- data/lib/easyhooks/store_values.rb +34 -0
- data/lib/easyhooks/trigger.rb +22 -0
- data/lib/easyhooks/version.rb +5 -0
- data/lib/easyhooks.rb +109 -0
- data/lib/generators/easyhooks/migration/migration_generator.rb +28 -0
- data/lib/generators/easyhooks/migration/templates/migration.rb +28 -0
- metadata +190 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
module Easyhooks
|
6
|
+
module Validators
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
ALLOWED_ON_VALUES = %i[create update destroy].freeze
|
10
|
+
ALLOWED_METHODS = %i[get post put patch delete].freeze
|
11
|
+
ALLOWED_TYPES = %i[default stored].freeze
|
12
|
+
|
13
|
+
included do
|
14
|
+
def validate_type(type)
|
15
|
+
return :default if type.nil?
|
16
|
+
raise TypeError, "Invalid #{self.class} type: #{type}" unless ALLOWED_TYPES.include?(type)
|
17
|
+
type
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_method(method)
|
21
|
+
return nil if method.nil? && @type == :stored # this will be loaded by the processor
|
22
|
+
return :post unless method.present?
|
23
|
+
method = method.downcase if method.is_a?(String)
|
24
|
+
raise TypeError, "Invalid method '#{method}' for #{self.class} '#{@name}'. Allowed values are: #{ALLOWED_METHODS}" unless ALLOWED_METHODS.include?(method.to_sym)
|
25
|
+
method.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_url?(url)
|
29
|
+
URI.parse(url) rescue false
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_endpoint(endpoint)
|
33
|
+
return nil if endpoint.nil? && @type == :stored # this will be loaded by the processor
|
34
|
+
raise TypeError, "#{self.class} endpoint can't be nil" unless endpoint.present?
|
35
|
+
raise TypeError, "#{self.class} endpoint is not a valid URL: #{endpoint}" unless valid_url?(endpoint)
|
36
|
+
endpoint
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_name(name)
|
40
|
+
raise TypeError, "#{self.class} name can't be nil" unless name.present?
|
41
|
+
raise TypeError, "Invalid #{self.class} name '#{name}'. Name can only have alphanumeric characters and underscore" unless name =~ /\A[a-zA-Z0-9_]+\z/
|
42
|
+
name
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_on(on)
|
46
|
+
return ALLOWED_ON_VALUES if on.nil?
|
47
|
+
on = [on] unless on.is_a?(Array)
|
48
|
+
on.map do |value|
|
49
|
+
raise TypeError, "Invalid attribute 'on' for #{self.class} #{@name}: #{on}. Allowed values are: #{ALLOWED_ON_VALUES}" unless ALLOWED_ON_VALUES.include?(value.to_sym)
|
50
|
+
value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_only(only)
|
55
|
+
return [] if only.nil?
|
56
|
+
only = [only] unless only.is_a?(Array)
|
57
|
+
only.map(&:to_sym) # convert only array into symbols array
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_callback(callback, attribute)
|
61
|
+
if callback.nil? || callback.is_a?(Symbol) || callback.respond_to?(:call)
|
62
|
+
callback
|
63
|
+
else
|
64
|
+
raise TypeError, "Invalid attribute '#{attribute}' for #{self.class} #{@name}: #{attribute} must be nil, an instance method name symbol or a callable (eg. a proc or lambda)"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_auth(auth)
|
69
|
+
return nil if auth.nil?
|
70
|
+
# validate if auth header is a string and it must be a Bearer token or a Basic auth to include into a HTTP request
|
71
|
+
raise TypeError, "Invalid attribute 'auth_header' for #{self.class} #{@name}: #{auth} must be nil or a string" unless auth.is_a?(String)
|
72
|
+
raise TypeError, "Invalid attribute 'auth_header' for #{self.class} #{@name}: #{auth} must be a Bearer token or a Basic auth" unless auth =~ /\ABearer\s/ || auth =~ /\ABasic\s/
|
73
|
+
auth
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_headers(headers)
|
77
|
+
return {} if headers.nil?
|
78
|
+
headers = JSON.parse(headers.gsub(':', '').gsub('=>', ':')) if headers.is_a?(String)
|
79
|
+
raise TypeError, "Invalid attribute 'headers' for #{self.class} #{@name}: #{headers} must be nil or a hash" unless headers.is_a?(Hash)
|
80
|
+
headers
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_hook(hook)
|
84
|
+
return hook if hook.nil? && @type == :stored
|
85
|
+
hook.method = validate_method(hook.method)
|
86
|
+
hook.endpoint = validate_endpoint(hook.endpoint)
|
87
|
+
hook.auth = validate_auth(hook.auth)
|
88
|
+
hook.headers = validate_headers(hook.headers)
|
89
|
+
hook
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Easyhooks
|
5
|
+
class PostProcessor < ActiveJob::Base
|
6
|
+
queue_as :easyhooks
|
7
|
+
|
8
|
+
def perform(klass_name, object_id, payload, action_name, action_trigger)
|
9
|
+
init_data(klass_name, object_id, payload, action_name, action_trigger)
|
10
|
+
begin
|
11
|
+
request = create_http_request
|
12
|
+
make_request(request)
|
13
|
+
trigger_event
|
14
|
+
rescue => e
|
15
|
+
if @action.on_fail.present? && @object.respond_to?(@action.on_fail_callable)
|
16
|
+
@object.send(@action.on_fail_callable, e)
|
17
|
+
else
|
18
|
+
raise e
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def init_data(klass_name, object_id, payload, action_name, action_trigger)
|
26
|
+
@klass_name = klass_name
|
27
|
+
@object = find_object(object_id)
|
28
|
+
@action_trigger = action_trigger
|
29
|
+
@action_name = action_name
|
30
|
+
load_action_and_payload(payload)
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_object(object_id)
|
34
|
+
@klass = @klass_name.constantize
|
35
|
+
@klass.find_by(id: object_id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def json?(value)
|
39
|
+
# check if value is a json string
|
40
|
+
JSON.parse(value)
|
41
|
+
true
|
42
|
+
rescue JSON::ParserError
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def enrich_data(data)
|
47
|
+
# if data is a hash or array or a json string
|
48
|
+
default = {
|
49
|
+
object: @klass_name,
|
50
|
+
action: @action.name,
|
51
|
+
trigger: {
|
52
|
+
name: @action.trigger_name,
|
53
|
+
event: @action_trigger.to_s.upcase
|
54
|
+
}
|
55
|
+
}
|
56
|
+
if json?(data)
|
57
|
+
default.merge({ data: JSON.parse(data) }).to_json
|
58
|
+
else
|
59
|
+
default.merge({ data: { id: data }}).to_json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_action_and_payload(payload)
|
64
|
+
@action = @klass.easyhooks_actions[@action_name]
|
65
|
+
@action.load!(@klass_name)
|
66
|
+
@payload = enrich_data(payload)
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_http_request
|
70
|
+
parsed_url = URI.parse(@action.hook.endpoint)
|
71
|
+
host = parsed_url.host
|
72
|
+
port = parsed_url.port
|
73
|
+
raise "Unable to load endpoint: #{@action.hook.endpoint}" unless host.present? && port.present?
|
74
|
+
|
75
|
+
@http = Net::HTTP.new(host, port)
|
76
|
+
@http.use_ssl = true if parsed_url.scheme == 'https'
|
77
|
+
|
78
|
+
Net::HTTP.const_get(@action.hook.method.to_s.capitalize).new(parsed_url.request_uri)
|
79
|
+
end
|
80
|
+
|
81
|
+
def make_request(request)
|
82
|
+
request.body = @payload
|
83
|
+
request.add_field('Content-Type', 'application/json')
|
84
|
+
|
85
|
+
# add headers
|
86
|
+
@action.hook.headers.each do |key, value|
|
87
|
+
request.add_field(key, value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# adds auth (bearer or basic)
|
91
|
+
request.add_field('Authorization', @action.hook.auth) if @action.hook.auth.present?
|
92
|
+
|
93
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE if Rails.env.test? # disable SSL verification for test env
|
94
|
+
@response = @http.request(request)
|
95
|
+
end
|
96
|
+
|
97
|
+
def trigger_event
|
98
|
+
@object.send(@action.event_callable, @response) if @object.respond_to?(@action.event_callable)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'easyhooks/action'
|
4
|
+
require 'easyhooks/trigger'
|
5
|
+
require 'easyhooks/hook'
|
6
|
+
require 'easyhooks/concerns/helpers'
|
7
|
+
require 'easyhooks/concerns/validators'
|
8
|
+
|
9
|
+
module Easyhooks
|
10
|
+
class Specification
|
11
|
+
include Easyhooks::Helpers
|
12
|
+
include Easyhooks::Validators
|
13
|
+
|
14
|
+
attr_accessor :name, :type, :global_args, :actions, :triggers, :scoped_action, :scoped_trigger
|
15
|
+
|
16
|
+
def initialize(name, type, args = {}, &specification)
|
17
|
+
@name = name
|
18
|
+
@type = type
|
19
|
+
@global_args = args.merge({ type: type })
|
20
|
+
@triggers = {}
|
21
|
+
@actions = {}
|
22
|
+
instance_eval(&specification)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def trigger(name, args = {}, &actions)
|
28
|
+
# merge args with global args keeping args as the priority
|
29
|
+
args = @global_args.merge(args)
|
30
|
+
|
31
|
+
type = args[:type]
|
32
|
+
|
33
|
+
hook_definition = find_trigger_hook(name, type, args)
|
34
|
+
|
35
|
+
# create the trigger
|
36
|
+
new_trigger = Easyhooks::Trigger.new(name, hook_definition, args)
|
37
|
+
@triggers[name] = new_trigger
|
38
|
+
@scoped_trigger = new_trigger
|
39
|
+
instance_eval(&actions) if actions
|
40
|
+
end
|
41
|
+
|
42
|
+
def action(name, args = {}, &event)
|
43
|
+
args = @scoped_trigger.args.merge(args)
|
44
|
+
type = args[:type]
|
45
|
+
|
46
|
+
hook_definition = find_action_hook(name, type, args)
|
47
|
+
|
48
|
+
# create the action
|
49
|
+
new_action = Easyhooks::Action.new(name, @scoped_trigger.name, hook_definition, args, &event)
|
50
|
+
@actions[name] = new_action
|
51
|
+
@scoped_action = new_action
|
52
|
+
@scoped_trigger.add_action(new_action)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_trigger_hook(name, type, args)
|
56
|
+
hook_definition = Hook.new
|
57
|
+
Hook::ATTRIBUTES.each do |field|
|
58
|
+
value = hook_lookup(:triggers, name, type, args, field) || hook_lookup(:classes, @name, type, args, field)
|
59
|
+
hook_definition.send("#{field}=".to_sym, value)
|
60
|
+
end
|
61
|
+
hook_definition
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_action_hook(name, type, args)
|
65
|
+
hook_definition = Hook.new
|
66
|
+
Hook::ATTRIBUTES.each do |field|
|
67
|
+
value = hook_lookup(:actions, name, type, args, field) || @scoped_trigger.hook.send(field)
|
68
|
+
hook_definition.send("#{field}=".to_sym, value)
|
69
|
+
end
|
70
|
+
hook_definition
|
71
|
+
end
|
72
|
+
|
73
|
+
def hook_lookup(attr_type, attr_name, type, args, field)
|
74
|
+
# stored triggers will load at post processor time
|
75
|
+
return nil if type == :stored
|
76
|
+
|
77
|
+
if args.present?
|
78
|
+
value = args[field]&.to_s
|
79
|
+
return value if value.present?
|
80
|
+
end
|
81
|
+
|
82
|
+
if config_file_exists? && [:method, :endpoint, :auth].include?(field)
|
83
|
+
value = config.dig(Rails.env, attr_type.to_s, attr_name.to_s, field.to_s)&.to_s
|
84
|
+
return value if value.present?
|
85
|
+
end
|
86
|
+
|
87
|
+
if config_file_exists? && field == :headers
|
88
|
+
value = config.dig(Rails.env, attr_type.to_s, attr_name.to_s, field.to_s)&.to_h
|
89
|
+
return value if value.present?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def config_file_exists?
|
94
|
+
@config_file_exists ||= File.exist?(config_file)
|
95
|
+
end
|
96
|
+
|
97
|
+
def config_file
|
98
|
+
args = 'config', 'easyhooks.yml'
|
99
|
+
args.unshift('test') if Rails.env.test?
|
100
|
+
|
101
|
+
@config_file ||= File.join(Rails.root, args)
|
102
|
+
end
|
103
|
+
|
104
|
+
def config
|
105
|
+
@config ||= YAML.load_file(config_file)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './store_values'
|
4
|
+
|
5
|
+
module Easyhooks
|
6
|
+
class Store < ActiveRecord::Base
|
7
|
+
self.table_name = 'easyhooks_store'
|
8
|
+
|
9
|
+
has_many :values, class_name: 'Easyhooks::StoreValues', dependent: :destroy
|
10
|
+
|
11
|
+
validates_presence_of :name, :method, :endpoint, :context
|
12
|
+
|
13
|
+
def add_headers(headers)
|
14
|
+
headers.each do |key, value|
|
15
|
+
values.create(context: 'headers', key: key, value: value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_auth(type, auth)
|
20
|
+
values.create(context: 'auth', key: type, value: auth)
|
21
|
+
end
|
22
|
+
|
23
|
+
def headers
|
24
|
+
values.where(context: 'headers').map { |v| [v.key, v.value] }.to_h
|
25
|
+
end
|
26
|
+
|
27
|
+
def auth
|
28
|
+
auth = values.where(context: 'auth').first
|
29
|
+
return nil unless auth.present?
|
30
|
+
|
31
|
+
"#{auth.key} #{auth.value}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# == Schema Information
|
37
|
+
#
|
38
|
+
# Table name: easyhooks_store
|
39
|
+
#
|
40
|
+
# id :integer not null, primary key
|
41
|
+
# context :string not null
|
42
|
+
# name :string not null
|
43
|
+
# method :string not null
|
44
|
+
# endpoint :string not null
|
45
|
+
# created_at :datetime
|
46
|
+
# updated_at :datetime
|
47
|
+
#
|
48
|
+
# Indexes
|
49
|
+
#
|
50
|
+
# index_easyhooks_store_on_name_and_context (name, context) UNIQUE
|
51
|
+
#
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './store'
|
4
|
+
|
5
|
+
module Easyhooks
|
6
|
+
class StoreValues < ActiveRecord::Base
|
7
|
+
self.table_name = 'easyhooks_store_values'
|
8
|
+
|
9
|
+
belongs_to :store, class_name: 'Easyhooks::Store'
|
10
|
+
|
11
|
+
validates_presence_of :key, :value, :context
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# == Schema Information
|
16
|
+
#
|
17
|
+
# Table name: easyhooks_store_values
|
18
|
+
#
|
19
|
+
# id :integer not null, primary key
|
20
|
+
# context :string not null
|
21
|
+
# key :string not null
|
22
|
+
# value :string not null
|
23
|
+
# easyhooks_store_id :integer not null
|
24
|
+
# created_at :datetime
|
25
|
+
# updated_at :datetime
|
26
|
+
#
|
27
|
+
# Indexes
|
28
|
+
#
|
29
|
+
# index_easyhooks_store_values_on_key_and_context (key, context)
|
30
|
+
#
|
31
|
+
# Foreign Keys
|
32
|
+
#
|
33
|
+
# fk_rails_... (easyhooks_store_id => easyhooks_store.id)
|
34
|
+
#
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'easyhooks/base'
|
4
|
+
|
5
|
+
module Easyhooks
|
6
|
+
class Trigger < Easyhooks::Base
|
7
|
+
|
8
|
+
attr_accessor :args, :on, :only, :actions
|
9
|
+
|
10
|
+
def initialize(name, hook, args = {})
|
11
|
+
super(name, args[:type], hook, args[:if], args[:payload], args[:on_fail])
|
12
|
+
@args = args
|
13
|
+
@on = validate_on(args[:on])
|
14
|
+
@only = validate_only(args[:only])
|
15
|
+
@actions = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_action(action)
|
19
|
+
@actions.push(action)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/easyhooks.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rails'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_record'
|
7
|
+
require 'active_job'
|
8
|
+
require 'easyhooks/specification'
|
9
|
+
require 'easyhooks/post_processor'
|
10
|
+
|
11
|
+
module Easyhooks
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
attr_reader :easyhooks_spec
|
15
|
+
|
16
|
+
def easyhooks(type = :default, args = {}, &specification)
|
17
|
+
if type.is_a?(Hash)
|
18
|
+
args = type
|
19
|
+
type = :default
|
20
|
+
end
|
21
|
+
# get self.class replacing :: from the module::class name with _
|
22
|
+
# e.g. MyModule::MyClass becomes MyModule_MyClass
|
23
|
+
klass_name = self.name.to_s.gsub('::', '_')
|
24
|
+
assign_easyhooks Specification.new(klass_name, type, args, &specification)
|
25
|
+
end
|
26
|
+
|
27
|
+
def easyhooks_actions
|
28
|
+
@easyhooks_spec.actions
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def assign_easyhooks(specification_object)
|
34
|
+
@easyhooks_spec = specification_object
|
35
|
+
|
36
|
+
@easyhooks_spec.actions.each do |_, action|
|
37
|
+
module_eval do
|
38
|
+
define_method action.event_callable do |response_data|
|
39
|
+
instance_exec(response_data, &action.event) if action.event.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
define_method action.on_fail_callable do |error|
|
43
|
+
send(action.on_fail, error)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods
|
51
|
+
extend ActiveSupport::Concern
|
52
|
+
|
53
|
+
included do
|
54
|
+
after_commit :dispatch_triggers
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def triggered_by
|
60
|
+
return :create if self.transaction_include_any_action?([:create])
|
61
|
+
|
62
|
+
return :update if self.transaction_include_any_action?([:update])
|
63
|
+
|
64
|
+
return :destroy if self.transaction_include_any_action?([:destroy])
|
65
|
+
|
66
|
+
:none
|
67
|
+
end
|
68
|
+
|
69
|
+
def perform_trigger_actions(trigger)
|
70
|
+
trigger.actions.each do |action|
|
71
|
+
next unless action.condition_applicable?(self)
|
72
|
+
payload = action.request_payload(self).to_json
|
73
|
+
PostProcessor.perform_later(self.class.name, self.id, payload, action.name, triggered_by)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute_trigger(trigger)
|
78
|
+
return unless trigger.condition_applicable?(self)
|
79
|
+
if trigger.only.empty? || triggered_by == :destroy
|
80
|
+
perform_trigger_actions(trigger)
|
81
|
+
else
|
82
|
+
trigger.only.each do |field|
|
83
|
+
perform_trigger_actions(trigger) if self.previous_changes.has_key?(field)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def dispatch_triggers
|
89
|
+
return unless self.class.easyhooks_spec
|
90
|
+
|
91
|
+
self.class.easyhooks_spec.triggers.each do |_, trigger|
|
92
|
+
execute_trigger(trigger) if self.transaction_include_any_action?(trigger.on)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.included(klass)
|
98
|
+
# check if the klass extends from ActiveRecord::Base, if not raise an error
|
99
|
+
unless klass.ancestors.include?(ActiveRecord::Base)
|
100
|
+
raise "Easyhooks can only be included in classes that extend from ActiveRecord::Base"
|
101
|
+
end
|
102
|
+
|
103
|
+
klass.send :include, InstanceMethods
|
104
|
+
|
105
|
+
klass.extend ClassMethods
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
ActiveRecord::Base.send(:include, Easyhooks)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module Easyhooks
|
5
|
+
class MigrationGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
desc 'Generates migration for easyhooks'
|
9
|
+
source_root File.expand_path('../templates', __FILE__)
|
10
|
+
|
11
|
+
def create_migration_file
|
12
|
+
migration_template 'migration.rb','db/migrate/easyhooks_migration.rb'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.next_migration_number(dirname)
|
16
|
+
if timestamped_migrations?
|
17
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S')
|
18
|
+
else
|
19
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.timestamped_migrations?
|
24
|
+
(ActiveRecord::Base.respond_to?(:timestamped_migrations) && ActiveRecord::Base.timestamped_migrations) ||
|
25
|
+
(ActiveRecord.respond_to?(:timestamped_migrations) && ActiveRecord.timestamped_migrations)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EasyhooksMigration < ActiveRecord::Migration[6.0]
|
4
|
+
def self.up
|
5
|
+
create_table :easyhooks_store do |t|
|
6
|
+
t.string :context, null: false
|
7
|
+
t.string :name, null: false
|
8
|
+
t.string :method, null: false
|
9
|
+
t.string :endpoint, null: false
|
10
|
+
t.timestamps null: true
|
11
|
+
end
|
12
|
+
add_index :easyhooks_store, %i[name context], unique: true
|
13
|
+
|
14
|
+
create_table :easyhooks_store_values do |t|
|
15
|
+
t.string :context, null: false
|
16
|
+
t.string :key, null: false
|
17
|
+
t.string :value, null: false
|
18
|
+
t.integer :store_id, null: false, references: :easyhooks_store
|
19
|
+
t.timestamps null: true
|
20
|
+
end
|
21
|
+
add_index :easyhooks_store_values, %i[context key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.down
|
25
|
+
drop_table :easyhooks_store_values
|
26
|
+
drop_table :easyhooks_store
|
27
|
+
end
|
28
|
+
end
|