global-registry-bindings 0.0.6 → 0.1.0
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 +4 -4
- data/README.md +79 -35
- data/lib/global_registry_bindings/entity/entity_type_methods.rb +35 -33
- data/lib/global_registry_bindings/entity/mdm_methods.rb +9 -19
- data/lib/global_registry_bindings/entity/push_entity_methods.rb +31 -38
- data/lib/global_registry_bindings/entity/push_relationship_methods.rb +61 -47
- data/lib/global_registry_bindings/entity/relationship_type_methods.rb +48 -37
- data/lib/global_registry_bindings/exceptions.rb +1 -0
- data/lib/global_registry_bindings/global_registry_bindings.rb +129 -46
- data/lib/global_registry_bindings/{entity/delete_entity_methods.rb → model/delete_entity.rb} +5 -5
- data/lib/global_registry_bindings/model/entity.rb +64 -0
- data/lib/global_registry_bindings/model/pull_mdm.rb +23 -0
- data/lib/global_registry_bindings/model/push_entity.rb +21 -0
- data/lib/global_registry_bindings/model/push_relationship.rb +79 -0
- data/lib/global_registry_bindings/model/relationship.rb +68 -0
- data/lib/global_registry_bindings/options.rb +26 -59
- data/lib/global_registry_bindings/options/entity_class_options.rb +34 -0
- data/lib/global_registry_bindings/options/entity_instance_options.rb +90 -0
- data/lib/global_registry_bindings/options/entity_options_parser.rb +76 -0
- data/lib/global_registry_bindings/options/relationship_class_options.rb +37 -0
- data/lib/global_registry_bindings/options/relationship_instance_options.rb +131 -0
- data/lib/global_registry_bindings/options/relationship_options_parser.rb +98 -0
- data/lib/global_registry_bindings/railtie.rb +2 -1
- data/lib/global_registry_bindings/version.rb +1 -1
- data/lib/global_registry_bindings/worker.rb +25 -0
- data/lib/global_registry_bindings/workers/{delete_gr_entity_worker.rb → delete_entity_worker.rb} +1 -6
- data/lib/global_registry_bindings/workers/pull_mdm_id_worker.rb +12 -13
- data/lib/global_registry_bindings/workers/push_entity_worker.rb +24 -0
- data/lib/global_registry_bindings/workers/push_relationship_worker.rb +17 -7
- data/spec/acceptance/global_registry_bindings_spec.rb +50 -40
- data/spec/factories/factories.rb +24 -0
- data/spec/fixtures/get_entity_types_area.json +44 -0
- data/spec/fixtures/post_entities_community.json +8 -0
- data/spec/fixtures/post_relationship_types_fancy_org_area.json +16 -0
- data/spec/fixtures/put_entities_community_relationship.json +16 -0
- data/spec/fixtures/put_entities_fancy_org_area_relationship.json +8 -0
- data/spec/fixtures/put_entities_fancy_org_relationship.json +17 -0
- data/spec/fixtures/put_entities_person_country_relationship.json +23 -0
- data/spec/fixtures/put_entities_relationship_400.json +3 -0
- data/spec/fixtures/put_relationship_types_fields_fancy_org_area.json +25 -0
- data/spec/helpers/sidekiq_helpers.rb +14 -0
- data/spec/internal/app/models/address.rb +7 -2
- data/spec/internal/app/models/area.rb +7 -0
- data/spec/internal/app/models/assignment.rb +5 -4
- data/spec/internal/app/models/community.rb +19 -0
- data/spec/internal/app/models/country.rb +8 -0
- data/spec/internal/app/models/namespaced/person.rb +48 -2
- data/spec/internal/app/models/organization.rb +26 -3
- data/spec/internal/db/schema.rb +28 -0
- data/spec/internal/log/test.log +71023 -0
- data/spec/models/address_spec.rb +6 -204
- data/spec/models/assignment_spec.rb +40 -186
- data/spec/models/organization_spec.rb +106 -92
- data/spec/models/person_spec.rb +158 -214
- data/spec/models/user_edited_person_spec.rb +2 -2
- data/spec/spec_helper.rb +5 -6
- data/spec/workers/delete_gr_entity_worker_spec.rb +4 -4
- data/spec/workers/pull_mdm_id_worker_spec.rb +94 -32
- data/spec/workers/push_entity_worker_spec.rb +476 -0
- data/spec/workers/push_relationship_worker_spec.rb +344 -15
- metadata +45 -10
- data/lib/global_registry_bindings/entity/entity_methods.rb +0 -62
- data/lib/global_registry_bindings/options/class_options.rb +0 -62
- data/lib/global_registry_bindings/options/instance_options.rb +0 -63
- data/lib/global_registry_bindings/workers/push_gr_entity_worker.rb +0 -22
- data/spec/workers/push_gr_entity_worker_spec.rb +0 -27
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'global_registry_bindings/workers/push_relationship_worker'
|
4
|
+
require 'global_registry_bindings/workers/delete_entity_worker'
|
5
|
+
|
6
|
+
module GlobalRegistry #:nodoc:
|
7
|
+
module Bindings #:nodoc:
|
8
|
+
module Model #:nodoc:
|
9
|
+
module PushRelationship
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
after_commit :push_relationships_to_global_registry_async, on: %i[create update]
|
14
|
+
after_commit :delete_relationships_from_global_registry_async, on: %i[destroy]
|
15
|
+
end
|
16
|
+
|
17
|
+
def push_relationships_to_global_registry_async(*types)
|
18
|
+
types = types.empty? ? self.class.global_registry_relationship_types : types
|
19
|
+
types.each do |type|
|
20
|
+
action = global_registry_relationship_change_action(type)
|
21
|
+
send("global_registry_relationship_async_#{action}".to_sym, type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_relationships_from_global_registry_async(*types)
|
26
|
+
types = types.empty? ? self.class.global_registry_relationship_types : types
|
27
|
+
types.each do |type|
|
28
|
+
next unless global_registry_relationship(type).id_value?
|
29
|
+
::GlobalRegistry::Bindings::Workers::DeleteEntityWorker.perform_async(
|
30
|
+
global_registry_relationship(type).id_value
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def global_registry_relationship_async_push(type)
|
38
|
+
::GlobalRegistry::Bindings::Workers::PushRelationshipWorker.perform_async(self.class, id, type)
|
39
|
+
end
|
40
|
+
|
41
|
+
def global_registry_relationship_async_replace(type)
|
42
|
+
# Replace deletes GR relationship immediately before scheduling an async update
|
43
|
+
::GlobalRegistry::Bindings::Workers::DeleteEntityWorker.new.perform(
|
44
|
+
global_registry_relationship(type).id_value
|
45
|
+
)
|
46
|
+
::GlobalRegistry::Bindings::Workers::PushRelationshipWorker.perform_async(self.class, id, type)
|
47
|
+
end
|
48
|
+
|
49
|
+
def global_registry_relationship_async_delete(type)
|
50
|
+
delete_relationships_from_global_registry_async(type)
|
51
|
+
end
|
52
|
+
|
53
|
+
def global_registry_relationship_async_ignore(_type); end # noop
|
54
|
+
|
55
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
56
|
+
# rubocop:disable Metrics/AbcSize
|
57
|
+
def global_registry_relationship_change_action(type)
|
58
|
+
[global_registry_relationship(type).primary_association_foreign_key,
|
59
|
+
global_registry_relationship(type).related_association_foreign_key].each do |key|
|
60
|
+
if previous_changes.key?(key)
|
61
|
+
# Delete if changed from anything to nil
|
62
|
+
return :delete if previous_changes[key].last.nil?
|
63
|
+
# Replace if value changed
|
64
|
+
return :replace if previous_changes[key].first != previous_changes[key].last &&
|
65
|
+
!previous_changes[key].first.nil?
|
66
|
+
elsif key.present? && send(key).nil?
|
67
|
+
# Ignore if value didn't change and foreign_key is nil
|
68
|
+
return :ignore
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# otherwise Create/Update
|
72
|
+
:push
|
73
|
+
end
|
74
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
75
|
+
# rubocop:enable Metrics/AbcSize
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GlobalRegistry #:nodoc:
|
4
|
+
module Bindings #:nodoc:
|
5
|
+
module Model #:nodoc:
|
6
|
+
module Relationship
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def relationship_attributes_to_push(type)
|
10
|
+
entity_attributes = relationship_columns_to_push(type).map do |name, t|
|
11
|
+
relationship_value_for_global_registry(name, t)
|
12
|
+
end.compact.to_h
|
13
|
+
unless global_registry_relationship(type).exclude_fields.include?(:client_integration_id)
|
14
|
+
entity_attributes[:client_integration_id] = global_registry_relationship(type).client_integration_id
|
15
|
+
end
|
16
|
+
entity_attributes[:client_updated_at] = updated_at.to_s(:db) if respond_to?(:updated_at)
|
17
|
+
entity_attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def relationship_value_for_global_registry(name, type)
|
21
|
+
value = send(name)
|
22
|
+
return [name, value] if value.nil?
|
23
|
+
value = case type
|
24
|
+
when :datetime, :date
|
25
|
+
value.to_s(:db)
|
26
|
+
when :boolean
|
27
|
+
value ? 'true' : 'false'
|
28
|
+
else
|
29
|
+
value.to_s.strip
|
30
|
+
end
|
31
|
+
[name, value]
|
32
|
+
rescue ::NoMethodError
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def relationship_columns_to_push(type)
|
37
|
+
@relationship_columns_to_push ||= {}
|
38
|
+
@relationship_columns_to_push[type] ||=
|
39
|
+
relationship_entity_columns(type)
|
40
|
+
.reject { |k, _v| global_registry_relationship(type).exclude_fields.include? k }
|
41
|
+
.merge(global_registry_relationship(type).extra_fields)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def normalize_relationship_column_type(type, name)
|
47
|
+
if type.to_s == 'text'
|
48
|
+
:string
|
49
|
+
elsif name.ends_with?('_id')
|
50
|
+
:uuid
|
51
|
+
else
|
52
|
+
type
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def relationship_entity_columns(type)
|
57
|
+
return {} if global_registry_relationship(type).primary_class_is_self?
|
58
|
+
self.class
|
59
|
+
.columns
|
60
|
+
.collect do |c|
|
61
|
+
{ c.name.underscore.to_sym => normalize_relationship_column_type(c.type, c.name) }
|
62
|
+
end
|
63
|
+
.reduce(&:merge)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'global_registry_bindings/options/
|
4
|
-
require 'global_registry_bindings/options/
|
3
|
+
require 'global_registry_bindings/options/entity_instance_options'
|
4
|
+
require 'global_registry_bindings/options/entity_class_options'
|
5
|
+
require 'global_registry_bindings/options/relationship_instance_options'
|
6
|
+
require 'global_registry_bindings/options/relationship_class_options'
|
5
7
|
|
6
8
|
module GlobalRegistry #:nodoc:
|
7
9
|
module Bindings #:nodoc:
|
@@ -9,74 +11,39 @@ module GlobalRegistry #:nodoc:
|
|
9
11
|
extend ActiveSupport::Concern
|
10
12
|
|
11
13
|
included do
|
14
|
+
# Entity Class Options
|
12
15
|
class_attribute :_global_registry_bindings_class_options
|
13
|
-
self._global_registry_bindings_class_options ||=
|
16
|
+
self._global_registry_bindings_class_options ||=
|
17
|
+
GlobalRegistry::Bindings::Options::EntityClassOptions.new(self)
|
18
|
+
# Relationship Class Options
|
19
|
+
class_attribute :_global_registry_bindings_class_relationships
|
20
|
+
self._global_registry_bindings_class_relationships = {}
|
14
21
|
end
|
15
22
|
|
16
|
-
def
|
17
|
-
@_global_registry_bindings_instance_options ||=
|
23
|
+
def global_registry_entity
|
24
|
+
@_global_registry_bindings_instance_options ||=
|
25
|
+
GlobalRegistry::Bindings::Options::EntityInstanceOptions.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def global_registry_relationship(type)
|
29
|
+
@_global_registry_bindings_instance_relationships ||= {}
|
30
|
+
@_global_registry_bindings_instance_relationships[type] ||=
|
31
|
+
GlobalRegistry::Bindings::Options::RelationshipInstanceOptions.new(type, self)
|
18
32
|
end
|
19
33
|
|
20
34
|
module ClassMethods
|
21
|
-
def
|
35
|
+
def global_registry_entity
|
22
36
|
_global_registry_bindings_class_options
|
23
37
|
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class OptionsParser
|
28
|
-
def initialize(model_class)
|
29
|
-
@model_class = model_class
|
30
|
-
end
|
31
|
-
|
32
|
-
def defaults
|
33
|
-
{
|
34
|
-
id_column: :global_registry_id,
|
35
|
-
mdm_id_column: nil,
|
36
|
-
type: @model_class.name.demodulize.underscore.to_sym,
|
37
|
-
push_on: %i[create update delete],
|
38
|
-
parent_association: nil,
|
39
|
-
parent_association_class: nil,
|
40
|
-
related_association: nil,
|
41
|
-
related_association_class: nil,
|
42
|
-
parent_relationship_name: nil,
|
43
|
-
related_relationship_name: nil,
|
44
|
-
exclude_fields: %i[id created_at updated_at],
|
45
|
-
extra_fields: {},
|
46
|
-
mdm_timeout: 1.minute
|
47
|
-
}.freeze
|
48
|
-
end
|
49
38
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
oldval.concat Array.wrap(newval)
|
54
|
-
else
|
55
|
-
newval
|
56
|
-
end
|
39
|
+
def global_registry_relationship(type)
|
40
|
+
_global_registry_bindings_class_relationships[type] ||=
|
41
|
+
GlobalRegistry::Bindings::Options::RelationshipClassOptions.new(type, self)
|
57
42
|
end
|
58
|
-
update_excludes
|
59
|
-
validate_options
|
60
|
-
@options
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def update_excludes
|
66
|
-
@options[:exclude_fields] << @options[:id_column]
|
67
|
-
@options[:exclude_fields] << @options[:mdm_id_column] if @options[:mdm_id_column].present?
|
68
|
-
|
69
|
-
parent_id_column = association_foreign_key @options[:parent_association]
|
70
|
-
@options[:exclude_fields] << parent_id_column.to_sym if parent_id_column
|
71
43
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def validate_options; end
|
77
|
-
|
78
|
-
def association_foreign_key(name)
|
79
|
-
@model_class.reflect_on_all_associations.detect { |a| a.name == name }&.foreign_key if name
|
44
|
+
def global_registry_relationship_types
|
45
|
+
_global_registry_bindings_options[:relationships].keys
|
46
|
+
end
|
80
47
|
end
|
81
48
|
end
|
82
49
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module GlobalRegistry #:nodoc:
|
6
|
+
module Bindings #:nodoc:
|
7
|
+
module Options
|
8
|
+
class EntityClassOptions
|
9
|
+
delegate :id_column,
|
10
|
+
:mdm_id_column,
|
11
|
+
:type,
|
12
|
+
:mdm_timeout,
|
13
|
+
:push_on,
|
14
|
+
:parent_association,
|
15
|
+
:parent_association_class,
|
16
|
+
:exclude_fields,
|
17
|
+
:extra_fields, to: :@options
|
18
|
+
|
19
|
+
def initialize(model_class)
|
20
|
+
@model_class = model_class
|
21
|
+
@options = OpenStruct.new model_class._global_registry_bindings_options[:entity]
|
22
|
+
end
|
23
|
+
|
24
|
+
def ensure_entity_type?
|
25
|
+
@options.ensure_entity_type.present?
|
26
|
+
end
|
27
|
+
|
28
|
+
def mdm_worker_class_name
|
29
|
+
"Pull#{@model_class.name.tr(':', '')}MdmIdWorker"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GlobalRegistry #:nodoc:
|
4
|
+
module Bindings #:nodoc:
|
5
|
+
module Options
|
6
|
+
class EntityInstanceOptions
|
7
|
+
delegate :id_column,
|
8
|
+
:mdm_id_column,
|
9
|
+
:mdm_timeout,
|
10
|
+
:push_on,
|
11
|
+
:parent_association,
|
12
|
+
:parent_association_class,
|
13
|
+
:mdm_worker_class_name,
|
14
|
+
:ensure_entity_type?,
|
15
|
+
to: :@class_options
|
16
|
+
|
17
|
+
def initialize(model)
|
18
|
+
@model = model
|
19
|
+
@class_options = model.class.global_registry_entity
|
20
|
+
end
|
21
|
+
|
22
|
+
def id_value
|
23
|
+
@model.send id_column
|
24
|
+
end
|
25
|
+
|
26
|
+
def id_value=(value)
|
27
|
+
@model.send "#{id_column}=", value
|
28
|
+
end
|
29
|
+
|
30
|
+
def id_value?
|
31
|
+
@model.send "#{id_column}?"
|
32
|
+
end
|
33
|
+
|
34
|
+
def type
|
35
|
+
t = @class_options.type
|
36
|
+
t.is_a?(Proc) ? t.call(@model) : t
|
37
|
+
end
|
38
|
+
|
39
|
+
def parent
|
40
|
+
@model.send(parent_association) if parent_association.present?
|
41
|
+
end
|
42
|
+
|
43
|
+
def parent_class
|
44
|
+
return if parent_association.blank?
|
45
|
+
parent_association_class
|
46
|
+
end
|
47
|
+
|
48
|
+
def parent_type
|
49
|
+
parent&.global_registry_entity&.type
|
50
|
+
end
|
51
|
+
|
52
|
+
def parent_id_value
|
53
|
+
parent&.global_registry_entity&.id_value
|
54
|
+
end
|
55
|
+
|
56
|
+
def parent_required?
|
57
|
+
parent_association.present? && !parent_is_self?
|
58
|
+
end
|
59
|
+
|
60
|
+
def parent_is_self?
|
61
|
+
parent_association.present? && parent_class == @model.class
|
62
|
+
end
|
63
|
+
|
64
|
+
def exclude_fields
|
65
|
+
option = @class_options.exclude_fields
|
66
|
+
case option
|
67
|
+
when Proc
|
68
|
+
option.call(type, @model)
|
69
|
+
when Symbol
|
70
|
+
@model.send(option, type)
|
71
|
+
else
|
72
|
+
option
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def extra_fields
|
77
|
+
option = @class_options.extra_fields
|
78
|
+
case option
|
79
|
+
when Proc
|
80
|
+
option.call(type, @model)
|
81
|
+
when Symbol
|
82
|
+
@model.send(option, type)
|
83
|
+
else
|
84
|
+
option
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GlobalRegistry #:nodoc:
|
4
|
+
module Bindings #:nodoc:
|
5
|
+
module Options
|
6
|
+
class EntityOptionsParser
|
7
|
+
def initialize(model_class)
|
8
|
+
@model_class = model_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def defaults
|
12
|
+
{
|
13
|
+
id_column: :global_registry_id,
|
14
|
+
mdm_id_column: nil,
|
15
|
+
type: @model_class.name.demodulize.underscore.to_sym,
|
16
|
+
push_on: %i[create update destroy],
|
17
|
+
parent_association: nil,
|
18
|
+
parent_association_class: nil,
|
19
|
+
parent_relationship_name: nil,
|
20
|
+
exclude_fields: %i[id created_at updated_at],
|
21
|
+
extra_fields: {},
|
22
|
+
mdm_timeout: 1.minute,
|
23
|
+
ensure_entity_type: true
|
24
|
+
}.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse(options_hash = {})
|
28
|
+
merge_defaults options_hash
|
29
|
+
update_association_classes
|
30
|
+
update_excludes
|
31
|
+
@options
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def merge_defaults(options_hash = {})
|
37
|
+
@options = defaults.merge(options_hash) do |key, oldval, newval|
|
38
|
+
if key == :exclude_fields
|
39
|
+
case newval
|
40
|
+
when Proc, Symbol
|
41
|
+
newval
|
42
|
+
else
|
43
|
+
oldval.concat Array.wrap(newval)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
newval
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_association_classes
|
52
|
+
unless @options[:parent_association_class] # rubocop:disable Style/GuardClause
|
53
|
+
@options[:parent_association_class] = association_class @options[:parent_association]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_excludes
|
58
|
+
return unless @options[:exclude_fields].is_a? Array
|
59
|
+
@options[:exclude_fields] << @options[:id_column]
|
60
|
+
@options[:exclude_fields] << @options[:mdm_id_column] if @options[:mdm_id_column].present?
|
61
|
+
|
62
|
+
parent_id_column = association_foreign_key @options[:parent_association]
|
63
|
+
@options[:exclude_fields] << parent_id_column.to_sym if parent_id_column
|
64
|
+
end
|
65
|
+
|
66
|
+
def association_foreign_key(name)
|
67
|
+
@model_class.reflect_on_association(name)&.foreign_key&.to_sym
|
68
|
+
end
|
69
|
+
|
70
|
+
def association_class(name)
|
71
|
+
@model_class.reflect_on_association(name)&.klass
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|