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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -35
  3. data/lib/global_registry_bindings/entity/entity_type_methods.rb +35 -33
  4. data/lib/global_registry_bindings/entity/mdm_methods.rb +9 -19
  5. data/lib/global_registry_bindings/entity/push_entity_methods.rb +31 -38
  6. data/lib/global_registry_bindings/entity/push_relationship_methods.rb +61 -47
  7. data/lib/global_registry_bindings/entity/relationship_type_methods.rb +48 -37
  8. data/lib/global_registry_bindings/exceptions.rb +1 -0
  9. data/lib/global_registry_bindings/global_registry_bindings.rb +129 -46
  10. data/lib/global_registry_bindings/{entity/delete_entity_methods.rb → model/delete_entity.rb} +5 -5
  11. data/lib/global_registry_bindings/model/entity.rb +64 -0
  12. data/lib/global_registry_bindings/model/pull_mdm.rb +23 -0
  13. data/lib/global_registry_bindings/model/push_entity.rb +21 -0
  14. data/lib/global_registry_bindings/model/push_relationship.rb +79 -0
  15. data/lib/global_registry_bindings/model/relationship.rb +68 -0
  16. data/lib/global_registry_bindings/options.rb +26 -59
  17. data/lib/global_registry_bindings/options/entity_class_options.rb +34 -0
  18. data/lib/global_registry_bindings/options/entity_instance_options.rb +90 -0
  19. data/lib/global_registry_bindings/options/entity_options_parser.rb +76 -0
  20. data/lib/global_registry_bindings/options/relationship_class_options.rb +37 -0
  21. data/lib/global_registry_bindings/options/relationship_instance_options.rb +131 -0
  22. data/lib/global_registry_bindings/options/relationship_options_parser.rb +98 -0
  23. data/lib/global_registry_bindings/railtie.rb +2 -1
  24. data/lib/global_registry_bindings/version.rb +1 -1
  25. data/lib/global_registry_bindings/worker.rb +25 -0
  26. data/lib/global_registry_bindings/workers/{delete_gr_entity_worker.rb → delete_entity_worker.rb} +1 -6
  27. data/lib/global_registry_bindings/workers/pull_mdm_id_worker.rb +12 -13
  28. data/lib/global_registry_bindings/workers/push_entity_worker.rb +24 -0
  29. data/lib/global_registry_bindings/workers/push_relationship_worker.rb +17 -7
  30. data/spec/acceptance/global_registry_bindings_spec.rb +50 -40
  31. data/spec/factories/factories.rb +24 -0
  32. data/spec/fixtures/get_entity_types_area.json +44 -0
  33. data/spec/fixtures/post_entities_community.json +8 -0
  34. data/spec/fixtures/post_relationship_types_fancy_org_area.json +16 -0
  35. data/spec/fixtures/put_entities_community_relationship.json +16 -0
  36. data/spec/fixtures/put_entities_fancy_org_area_relationship.json +8 -0
  37. data/spec/fixtures/put_entities_fancy_org_relationship.json +17 -0
  38. data/spec/fixtures/put_entities_person_country_relationship.json +23 -0
  39. data/spec/fixtures/put_entities_relationship_400.json +3 -0
  40. data/spec/fixtures/put_relationship_types_fields_fancy_org_area.json +25 -0
  41. data/spec/helpers/sidekiq_helpers.rb +14 -0
  42. data/spec/internal/app/models/address.rb +7 -2
  43. data/spec/internal/app/models/area.rb +7 -0
  44. data/spec/internal/app/models/assignment.rb +5 -4
  45. data/spec/internal/app/models/community.rb +19 -0
  46. data/spec/internal/app/models/country.rb +8 -0
  47. data/spec/internal/app/models/namespaced/person.rb +48 -2
  48. data/spec/internal/app/models/organization.rb +26 -3
  49. data/spec/internal/db/schema.rb +28 -0
  50. data/spec/internal/log/test.log +71023 -0
  51. data/spec/models/address_spec.rb +6 -204
  52. data/spec/models/assignment_spec.rb +40 -186
  53. data/spec/models/organization_spec.rb +106 -92
  54. data/spec/models/person_spec.rb +158 -214
  55. data/spec/models/user_edited_person_spec.rb +2 -2
  56. data/spec/spec_helper.rb +5 -6
  57. data/spec/workers/delete_gr_entity_worker_spec.rb +4 -4
  58. data/spec/workers/pull_mdm_id_worker_spec.rb +94 -32
  59. data/spec/workers/push_entity_worker_spec.rb +476 -0
  60. data/spec/workers/push_relationship_worker_spec.rb +344 -15
  61. metadata +45 -10
  62. data/lib/global_registry_bindings/entity/entity_methods.rb +0 -62
  63. data/lib/global_registry_bindings/options/class_options.rb +0 -62
  64. data/lib/global_registry_bindings/options/instance_options.rb +0 -63
  65. data/lib/global_registry_bindings/workers/push_gr_entity_worker.rb +0 -22
  66. data/spec/workers/push_gr_entity_worker_spec.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc5f397574e48c4f792014e06ab828b3ff015663
4
- data.tar.gz: 6907779fa74f374191b706e28ffd9253dfd0ea23
3
+ metadata.gz: 451f38a412ca7fd1e2b05ff8e96d56deb1b672a8
4
+ data.tar.gz: 9af6074f11baf7031348356041f970dae848241f
5
5
  SHA512:
6
- metadata.gz: bedf745fc6e7c872315b457ca1edd8f74a98bdea2df0e1e086d9137b11346c59b753fa49ccde821fed2cfc1aab2c795f9cecda6f6e2ea964e504e08d322659c7
7
- data.tar.gz: c8422e9859e07b0105f4b16294bd28207097b810423962c2318024ba3aadcc590ef3500948bc4cbe04c8a4951a86dbcb65f433def74f837739785f3b9966af44
6
+ metadata.gz: 4d496aebf53060214bf787551c9b45b306ebf0a333f9840dc33383d17faa8d19345b9d5563a46f3a5af2f4831cd1ac505ab9e2ec22f9e68cc6e82b077bd09ad5
7
+ data.tar.gz: 06f34a68d132652485564106595954833b359e8c0054b679f09227aff5923ae08ffe5f3212d08863da828605bf4b9871150d76ccf625a92fef6049d5ff370580
data/README.md CHANGED
@@ -49,32 +49,90 @@ end
49
49
 
50
50
  You can pass various options to the `global_registry_bindings` method. Configuration options are:
51
51
 
52
- * `:id_column`: Column used to track the Global Registry ID for the model instance. Can be a :string or :uuid column.
53
- (default: `:global_registry_id`)
54
- * `:mdm_id_column`: Column used to enable MDM tracking and set the name of the column. MDM is disabled when this
55
- option is nil or empty. (default: `nil`)
56
- * `:type`: Global Registry entity type. Default value is underscored name of the model.
52
+ * `:binding`: Type of Global Registry binding. Either `:entity` or `:relationship`.
53
+ (default: `:entity`)
54
+
55
+ * `:id_column`: Column used to track the Global Registry ID for the model instance or relationship entity.
56
+ Can be a :string or :uuid column. (default: `:global_registry_id`) **[`:entity`, `:relationship`]**
57
+
58
+ * `:type`: Global Registry entity type. Accepts a Symbol or a Proc. Symbol is the name of the entity type, Proc
59
+ is passed the model instance and must return a symbol which is the entity type. Default value is underscored
60
+ name of the model. Ex: ```type: proc { |model| model.name.to_sym }```. When used in a `:relationship`, `:type`
61
+ is a unique name to identify the relationship. **[`:entity`, `:relationship`]**
62
+
57
63
  * `:push_on`: Array of Active Record lifecycle events used to push changes to Global Registry.
58
- (default: `[:create, :update, :delete]`)
64
+ (default: `[:create, :update, :destroy]`) **[`:entity`]**
65
+
59
66
  * `:parent_association`: Name of the Active Record parent association. Must be defined before calling
60
- global_registry_bindings in order to determine foreign_key field. (default: `nil`)
67
+ global_registry_bindings in order to determine foreign_key for use in exclude_fields. Used to create a
68
+ hierarchy or to push child entity types. (Ex: person -> address) (default: `nil`) **[`:entity`]**
69
+
61
70
  * `:parent_association_class`: Class name of the parent model. Required if `:parent_association` can not be used
62
- to determine the parent class. This can happen if parent is defined by another gem, like `has_ancestry`.
63
- (default: `nil`)
64
- * `:related_association`: Name of the Active Record related association. Setting this option changes the
65
- global registry binding from entity to relationship. Active Record association must be defined before calling
66
- global_registry_bindings in order to determine the foreign key. `:parent_relationship_name` and
67
- `:related_relationship_name` must be set for relationship binding to work. (default: `nil`)
71
+ to determine the parent class. This can happen if parent is defined by another gem, like `ancestry`.
72
+ (default: `nil`) **[`:entity`]**
73
+
74
+ * `:primary_association`: Name of the Active Record primary association. Must be defined before calling
75
+ global_registry_bindings in order to determine foreign_key for use in exclude_fields. (default: `nil`)
76
+ **[`:relationship`]**
77
+
78
+ * `:primary_association_class`: Class name of the primary model. Required if `:primary_association` can not be
79
+ used to determine the parent class. This can happen if parent is defined by another gem, like `ancestry`.
80
+ (default: `self.class`) **[`:relationship`]**
81
+
82
+ * `:primary_association_foreign_key`: Foreign Key column for the primary association. Used if foreign_key can
83
+ not be determined from `:primary_association`. (default: `:primary_association.foreign_key`)
84
+ **[`:relationship`]**
85
+
86
+ * `:related_association`: Name of the Active Record related association. Active Record association must be
87
+ defined before calling global_registry_bindings in order to determine the foreign key.
88
+ (default: `nil`) **[`:relationship`]**
89
+
68
90
  * `:related_association_class`: Class name of the related model. Required if `:related_association` can not be
69
- used to determine the related class. (default: `nil`)
70
- * `:parent_relationship_name`: Name of parent relationship role. (default: `nil`)
71
- * `:related_relationship_name`: Name of the related relationship role. (default: `nil`)
72
- * `:exclude_fields`: Model fields to exclude when pushing to Global Registry. Will additionally include `:mdm_id_column`
73
- and `:parent_association` foreign key when defined.
74
- (default: `[:id, :created_at, :updated_at, :global_registry_id]`)
75
- * `:extra_fields`: Additional fields to send to Global Registry. This should be a hash with name as the key
76
- and :type attributes as the value. Ex: `{language: :string}`. Name is a symbol and type is an ActiveRecord column type.
91
+ used to determine the related class. (default: `nil`) **[`:relationship`]**
92
+
93
+ * `:related_association_foreign_key`: Foreign Key column for the related association. Used if foreign_key can
94
+ not be determined from `:primary_association`. (default: `:primary_association.foreign_key`)
95
+ **[`:relationship`]**
96
+
97
+ * `:primary_relationship_name`: Name of primary relationship role. Required if `:ensure_relationship_type` is
98
+ `true`. (default: `nil`) **[`:relationship`]**
99
+
100
+ * `:related_relationship_name`: Name of the related relationship role. Required if `:ensure_relationship_type`
101
+ is `true`. (default: `nil`) **[`:relationship`]**
102
+
103
+ * `:related_association_type`: Name of the related association entity_type. Required if unable to determined
104
+ `:type` from related. (default: `nil`) **[`:relationship`]**
105
+
106
+ * `:related_global_registry_id`: Global Registry ID of a remote related entity. Proc or Symbol. Implementation
107
+ should cache this as it may be requested multiple times. (default: `nil`) **[`:relationship`]**
108
+
109
+ * `:ensure_relationship_type`: Ensure Global Registry RelationshipType exists and is up to date.
110
+ (default: `true`) **[`:relationship`]**
111
+
112
+ * `:ensure_entity_type`: Ensure Global Registry Entity Type exists and is up to date.
113
+ (default: `true`) **[`:entity`]**
114
+
115
+ * `:client_integration_id`: Client Integration ID for relationship. Proc or Symbol.
116
+ (default: `:primary_association.id`) **[`:relationship`]**
117
+
118
+ * `:exclude_fields`: Array, Proc or Symbol. Array of Model fields (as symbols) to exclude when pushing to Global
119
+ Registry. Array Will additionally include `:mdm_id_column` and `:parent_association` foreign key when defined.
120
+ If Proc, is passed type and model instance and should return an Array of the fields to exclude. If Symbol,
121
+ this should be a method name the Model instance responds to. It is passed the type and should return an Array
122
+ of fields to exclude. When Proc or Symbol are used, you must explicitly return the standard defaults.
123
+ (default: `[:id, :created_at, :updated_at, :global_registry_id]`) **[`:entity`, `:relationship`]**
124
+
125
+ * `:extra_fields`: Additional fields to send to Global Registry. Hash, Proc or Symbol. As a Hash, names are the
126
+ keys and :type attributes are the values. Ex: `{language: :string}`. Name is a symbol and type is an
127
+ ActiveRecord column type. As a Proc, it is passed the type and model instance, and should return a Hash.
128
+ As a Symbol, the model should respond to this method, is passed the type, and should return a Hash.
129
+ **[`:entity`, `:relationship`]**
130
+
131
+ * `:mdm_id_column`: Column used to enable MDM tracking and set the name of the column. MDM is disabled when this
132
+ option is nil or empty. (default: `nil`) **[`:entity`]**
133
+
77
134
  * `:mdm_timeout`: Only pull mdm information at most once every `:mdm_timeout`. (default: `1.minute`)
135
+ **[`:entity`]**
78
136
 
79
137
  ## Values for `extra_fields`
80
138
 
@@ -106,20 +164,6 @@ class Person < ActiveRecord::Base
106
164
  end
107
165
  ```
108
166
 
109
- ## Relationships
110
-
111
- Global Registry allows for relating two entities together (many-to-many) through a relationship. An example of this
112
- could be a Person to Person relationship. This relationship could be described as husband/spouse, or even
113
- leader/employee. You could also relate a Person to an Organization through an assignment. The assignment can track
114
- specific fields about the relationship.
115
-
116
- `global-registry-bindings` supports this through the `:parent_association` and `:related_association` options.
117
- Relationship roles, like husband/wife, are defined through the `:parent_relationship_name` and
118
- `:related_relationship_name` options.
119
-
120
- More information on Global Registry relationships and relationship types can be found
121
- [here](https://github.com/CruGlobal/global_registry_docs/wiki/About-Relationships)
122
-
123
167
  ## Example Models
124
168
 
125
169
  Example models can be found in the [specs](https://github.com/CruGlobal/global-registry-bindings/tree/master/spec/internal/app/models).
@@ -8,46 +8,48 @@ module GlobalRegistry #:nodoc:
8
8
  module EntityTypeMethods
9
9
  extend ActiveSupport::Concern
10
10
 
11
- module ClassMethods
12
- def push_entity_type
13
- parent_entity_id = parent_entity_type_id
14
- entity_type = Rails.cache.fetch(entity_type_cache_key, expires_in: 1.hour) do
15
- GlobalRegistry::EntityType.get('filters[name]' => global_registry.type,
16
- 'filters[parent_id]' => parent_entity_id)['entity_types']&.first
17
- end
18
-
19
- unless entity_type
20
- entity_type = GlobalRegistry::EntityType.post(entity_type: { name: global_registry.type,
21
- parent_id: parent_entity_id,
22
- field_type: 'entity' })['entity_type']
23
- end
24
-
25
- push_entity_type_fields(entity_type)
26
- entity_type
11
+ def push_entity_type_to_global_registry
12
+ return unless global_registry_entity.ensure_entity_type?
13
+ parent_entity_id = parent_entity_type_id
14
+ entity_type = Rails.cache.fetch(entity_type_cache_key, expires_in: 1.hour) do
15
+ GlobalRegistry::EntityType.get('filters[name]' => global_registry_entity.type,
16
+ 'filters[parent_id]' => parent_entity_id)['entity_types']&.first
27
17
  end
28
18
 
29
- def push_entity_type_fields(entity_type)
30
- existing_fields = entity_type['fields']&.collect { |f| f['name'].to_sym } || []
31
- columns_to_push
32
- .reject { |k, _v| existing_fields.include? k }
33
- .each do |name, type|
34
- GlobalRegistry::EntityType.post(entity_type: { name: name,
35
- parent_id: entity_type['id'],
36
- field_type: type })
37
- end
19
+ unless entity_type
20
+ entity_type = GlobalRegistry::EntityType.post(entity_type: { name: global_registry_entity.type,
21
+ parent_id: parent_entity_id,
22
+ field_type: 'entity' })['entity_type']
38
23
  end
39
24
 
40
- def parent_entity_type_id
41
- parent_type = global_registry&.parent_type
42
- return if parent_type.blank? || global_registry.parent_is_self?
43
- parent_entity_type = global_registry.parent_class.send :push_entity_type
44
- parent_entity_type&.dig('id')
45
- end
25
+ push_entity_type_fields_to_global_registry(entity_type)
26
+ entity_type
27
+ end
46
28
 
47
- def entity_type_cache_key
48
- "GlobalRegistry::Bindings::EntityType::#{global_registry.type}"
29
+ private
30
+
31
+ def push_entity_type_fields_to_global_registry(entity_type)
32
+ existing_fields = entity_type['fields']&.collect { |f| f['name'].to_sym } || []
33
+ model.entity_columns_to_push
34
+ .reject { |k, _v| existing_fields.include? k }
35
+ .each do |name, type|
36
+ GlobalRegistry::EntityType.post(entity_type: { name: name,
37
+ parent_id: entity_type['id'],
38
+ field_type: type })
49
39
  end
50
40
  end
41
+
42
+ def parent_entity_type_id
43
+ parent = global_registry_entity&.parent
44
+ return if parent.blank? || global_registry_entity.parent_is_self?
45
+ worker = GlobalRegistry::Bindings::Workers::PushEntityWorker.new parent
46
+ parent_entity_type = worker.send :push_entity_type_to_global_registry
47
+ parent_entity_type&.dig('id')
48
+ end
49
+
50
+ def entity_type_cache_key
51
+ "GlobalRegistry::Bindings::EntityType::#{global_registry_entity.type}"
52
+ end
51
53
  end
52
54
  end
53
55
  end
@@ -1,37 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'global_registry_bindings/workers/pull_mdm_id_worker'
4
-
5
3
  module GlobalRegistry #:nodoc:
6
4
  module Bindings #:nodoc:
7
5
  module Entity #:nodoc:
8
6
  module MdmMethods
9
7
  extend ActiveSupport::Concern
10
8
 
11
- included do
12
- GlobalRegistry::Bindings::Workers.mdm_worker_class(self)
13
- after_commit :pull_mdm_id_from_global_registry_async, on: %i[create update]
14
- end
15
-
16
- def pull_mdm_id_from_global_registry_async
17
- "::GlobalRegistry::Bindings::Workers::#{global_registry.mdm_worker_class_name}".constantize
18
- .perform_async(self.class, id)
19
- end
20
-
21
9
  def pull_mdm_id_from_global_registry # rubocop:disable Metrics/AbcSize
22
- unless global_registry.id_value?
10
+ unless global_registry_entity.id_value?
23
11
  # Record missing Global Registry ID, enqueue it to be pushed.
24
- push_entity_to_global_registry_async
12
+ model.push_entity_to_global_registry_async
25
13
  raise GlobalRegistry::Bindings::RecordMissingGlobalRegistryId,
26
- "#{self.class.name} #{id} has no #{global_registry.id_column}; will retry"
14
+ "#{model.class.name}(#{model.id}) has no #{global_registry_entity.id_column}; will retry"
27
15
  end
28
- entity = GlobalRegistry::Entity.find(global_registry.id_value, 'filters[owned_by]' => 'mdm')
29
- mdm_entity_id = dig_global_registry_mdm_id_from_entity(entity, global_registry.type.to_s)
16
+ entity = GlobalRegistry::Entity.find(global_registry_entity.id_value, 'filters[owned_by]' => 'mdm')
17
+ mdm_entity_id = dig_global_registry_mdm_id_from_entity(entity, global_registry_entity.type.to_s)
30
18
  unless mdm_entity_id
31
19
  raise GlobalRegistry::Bindings::EntityMissingMdmId,
32
- "GR entity #{global_registry.id_value} for #{self.class.name} #{id} has no mdm id; will retry"
20
+ "GR entity #{global_registry_entity.id_value} for #{model.class.name}(#{model.id}) has no mdm id; " \
21
+ 'will retry'
33
22
  end
34
- update_column(global_registry.mdm_id_column, mdm_entity_id) # rubocop:disable Rails/SkipsModelValidations
23
+ # rubocop:disable Rails/SkipsModelValidations
24
+ model.update_column(global_registry_entity.mdm_id_column, mdm_entity_id)
35
25
  end
36
26
 
37
27
  def dig_global_registry_mdm_id_from_entity(entity, type)
@@ -1,82 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'global_registry_bindings/workers/push_gr_entity_worker'
4
-
5
3
  module GlobalRegistry #:nodoc:
6
4
  module Bindings #:nodoc:
7
5
  module Entity #:nodoc:
8
6
  module PushEntityMethods
9
7
  extend ActiveSupport::Concern
10
8
 
11
- included do
12
- after_commit :push_entity_to_global_registry_async, on: (global_registry.push_on - %i[delete])
13
- end
14
-
15
- def push_entity_to_global_registry_async
16
- ::GlobalRegistry::Bindings::Workers::PushGrEntityWorker.perform_async(self.class, id)
17
- end
18
-
19
- def push_entity_to_global_registry
20
- self.class.push_entity_type
9
+ def push_entity_to_global_registry # rubocop:disable Metrics/PerceivedComplexity
10
+ return if global_registry_entity.parent_required? && global_registry_entity.parent.blank?
11
+ push_entity_type_to_global_registry
21
12
 
22
- if global_registry.parent_type.present? && !global_registry.parent_is_self?
13
+ if global_registry_entity.parent_type.present? && !global_registry_entity.parent_is_self?
23
14
  create_dependent_entity_in_global_registry
24
- elsif global_registry.id_value?
15
+ elsif global_registry_entity.id_value?
25
16
  update_entity_in_global_registry
26
17
  else
27
18
  create_entity_in_global_registry
28
19
  end
29
20
  rescue RestClient::ResourceNotFound
30
- global_registry.id_value = nil
21
+ global_registry_entity.id_value = nil
31
22
  push_entity_to_global_registry
32
23
  end
33
24
 
34
25
  def update_entity_in_global_registry
35
- entity_attributes = { global_registry.type => entity_attributes_to_push }
36
- GlobalRegistry::Entity.put(global_registry.id_value, entity: entity_attributes)
26
+ entity_attributes = { global_registry_entity.type => model.entity_attributes_to_push }
27
+ GlobalRegistry::Entity.put(global_registry_entity.id_value, entity: entity_attributes)
37
28
  end
38
29
 
39
30
  def create_entity_in_global_registry
40
- ensure_parent_entity_has_global_registry_id! if global_registry.parent.present?
41
- entity_attributes = { global_registry.type => entity_attributes_to_push }
31
+ ensure_parent_entity_has_global_registry_id! if global_registry_entity.parent.present?
32
+ entity_attributes = { global_registry_entity.type => model.entity_attributes_to_push }
42
33
  entity = GlobalRegistry::Entity.post(entity: entity_attributes)
43
- global_registry.id_value = dig_global_registry_id_from_entity(entity['entity'], global_registry.type)
44
- update_column(global_registry.id_column, # rubocop:disable Rails/SkipsModelValidations
45
- global_registry.id_value)
34
+ global_registry_entity.id_value = dig_global_registry_id_from_entity(entity['entity'],
35
+ global_registry_entity.type)
36
+ model.update_column(global_registry_entity.id_column, # rubocop:disable Rails/SkipsModelValidations
37
+ global_registry_entity.id_value)
46
38
  end
47
39
 
48
40
  # Create or Update a child entity (ex: :email_address is a child of :person)
49
41
  def create_dependent_entity_in_global_registry # rubocop:disable Metrics/AbcSize
50
- return if global_registry.parent.blank?
51
- global_registry.parent.push_entity_to_global_registry if global_registry.parent_id_value.blank?
42
+ return if global_registry_entity.parent.blank?
43
+ ensure_parent_entity_has_global_registry_id!
52
44
  entity_attributes = {
53
- global_registry.parent_type => {
54
- client_integration_id: global_registry.parent.id,
55
- global_registry.type => entity_attributes_to_push
45
+ global_registry_entity.parent_type => {
46
+ client_integration_id: global_registry_entity.parent.id,
47
+ global_registry_entity.type => model.entity_attributes_to_push
56
48
  }
57
49
  }
58
- entity = GlobalRegistry::Entity.put(global_registry.parent_id_value, entity: entity_attributes)
59
- global_registry.id_value = dig_global_registry_id_from_entity(entity['entity'],
60
- global_registry.type,
61
- global_registry.parent_type)
62
- update_column(global_registry.id_column, # rubocop:disable Rails/SkipsModelValidations
63
- global_registry.id_value)
50
+ entity = GlobalRegistry::Entity.put(global_registry_entity.parent_id_value, entity: entity_attributes)
51
+ global_registry_entity.id_value = dig_global_registry_id_from_entity(entity['entity'],
52
+ global_registry_entity.type,
53
+ global_registry_entity.parent_type)
54
+ model.update_column(global_registry_entity.id_column, # rubocop:disable Rails/SkipsModelValidations
55
+ global_registry_entity.id_value)
64
56
  end
65
57
 
66
58
  def dig_global_registry_id_from_entity(entity, type, parent_type = nil)
67
59
  return entity&.dig(type.to_s, 'id') unless parent_type
68
60
  Array.wrap(entity&.dig(parent_type.to_s, type.to_s)).detect do |item|
69
- item['client_integration_id'] == id.to_s
61
+ item['client_integration_id'] == model.id.to_s
70
62
  end&.dig('id')
71
63
  end
72
64
 
73
65
  def ensure_parent_entity_has_global_registry_id!
74
- return unless global_registry.parent_is_self? && global_registry.parent_id_value.blank?
66
+ return unless (global_registry_entity.parent_is_self? && global_registry_entity.parent_id_value.blank?) ||
67
+ global_registry_entity.parent_id_value.blank?
75
68
  # Push parent entity if it exists and is missing global_registry_id
76
- global_registry.parent.push_entity_to_global_registry_async
69
+ global_registry_entity.parent.push_entity_to_global_registry_async
77
70
  raise GlobalRegistry::Bindings::ParentEntityMissingGlobalRegistryId,
78
- "#{self.class.name}(#{id}) has parent entity " \
79
- "#{global_registry.parent.class.name}(#{global_registry.parent.id}) missing " \
71
+ "#{model.class.name}(#{model.id}) has parent entity " \
72
+ "#{global_registry_entity.parent.class.name}(#{global_registry_entity.parent.id}) missing " \
80
73
  'global_registry_id; will retry.'
81
74
  end
82
75
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'global_registry'
4
- require 'global_registry_bindings/workers/push_relationship_worker'
4
+ require 'global_registry_bindings/workers/delete_entity_worker'
5
5
 
6
6
  module GlobalRegistry #:nodoc:
7
7
  module Bindings #:nodoc:
@@ -9,71 +9,85 @@ module GlobalRegistry #:nodoc:
9
9
  module PushRelationshipMethods
10
10
  extend ActiveSupport::Concern
11
11
 
12
- included do
13
- after_commit :push_relationship_to_global_registry_async, on: (global_registry.push_on - %i[delete])
14
- end
15
-
16
- def push_relationship_to_global_registry_async
17
- ::GlobalRegistry::Bindings::Workers::PushRelationshipWorker.perform_async(self.class, id)
18
- end
19
-
20
12
  def push_relationship_to_global_registry
21
13
  ensure_related_entities_have_global_registry_ids!
22
- self.class.push_global_registry_relationship_type
23
-
24
- if global_registry.id_value?
25
- update_relationship_in_global_registry
26
- else
27
- create_relationship_in_global_registry
28
- end
29
- end
30
-
31
- def update_relationship_in_global_registry
32
- GlobalRegistry::Entity.put(global_registry.id_value, entity: entity_attributes_to_push)
14
+ push_global_registry_relationship_type
15
+ create_relationship_in_global_registry
33
16
  end
34
17
 
35
- def create_relationship_in_global_registry # rubocop:disable Metrics/AbcSize
36
- entity = GlobalRegistry::Entity.put(global_registry.parent_id_value,
37
- { entity: { global_registry.parent_type => {
38
- "#{global_registry.related_relationship_name}:relationship" =>
39
- entity_attributes_to_push.merge(global_registry.related_type =>
40
- global_registry.related_id_value)
41
- }, client_integration_id: global_registry.parent.id } },
42
- params: {
43
- full_response: true,
44
- fields: "#{global_registry.related_relationship_name}:relationship"
45
- })
46
- global_registry.id_value = global_registry_relationship_entity_id_from_entity entity
47
- update_column(global_registry.id_column, # rubocop:disable Rails/SkipsModelValidations
48
- global_registry.id_value)
49
- # Update relationship to work around bug in Global Registry
50
- # - If current system doesn't own a copy of the parent entity, then creating a new relationship in the same
51
- # request will not add the relationship entity_type properties.
52
- update_relationship_in_global_registry if global_registry.id_value?
18
+ def create_relationship_in_global_registry
19
+ entity = put_relationship_to_global_registry
20
+ global_registry_relationship(type).id_value = global_registry_relationship_entity_id_from_entity entity
21
+ model.update_column( # rubocop:disable Rails/SkipsModelValidations
22
+ global_registry_relationship(type).id_column,
23
+ global_registry_relationship(type).id_value
24
+ )
53
25
  end
54
26
 
55
27
  def global_registry_relationship_entity_id_from_entity(entity)
56
- relationships = Array.wrap entity.dig('entity', global_registry.parent_type.to_s,
57
- "#{global_registry.related_relationship_name}:relationship")
28
+ relationships = Array.wrap entity.dig(
29
+ 'entity',
30
+ global_registry_relationship(type).primary_type.to_s,
31
+ "#{global_registry_relationship(type).related_relationship_name}:relationship"
32
+ )
58
33
  relationships.detect do |rel|
59
34
  cid = rel['client_integration_id']
60
35
  cid = cid['value'] if cid.is_a?(Hash)
61
- cid == id.to_s
36
+ cid == global_registry_relationship(type).client_integration_id.to_s
62
37
  end&.dig('relationship_entity_id')
63
38
  end
64
39
 
65
- def ensure_related_entities_have_global_registry_ids!
66
- return if global_registry.parent_id_value.present? && global_registry.related_id_value.present?
40
+ def ensure_related_entities_have_global_registry_ids! # rubocop:disable Metrics/AbcSize
41
+ if global_registry_relationship(type).primary_id_value.present? &&
42
+ global_registry_relationship(type).related_id_value.present?
43
+ return
44
+ end
67
45
  # Enqueue push_entity worker for related entities missing global_registry_id and retry relationship push
68
46
  names = []
69
- [global_registry.parent, global_registry.related].each do |model|
70
- next if model.global_registry.id_value?
47
+ [global_registry_relationship(type).primary, global_registry_relationship(type).related].each do |model|
48
+ next if model.global_registry_entity.id_value?
71
49
  names << "#{model.class.name}(#{model.id})"
72
50
  model.push_entity_to_global_registry_async
73
51
  end
74
52
  raise GlobalRegistry::Bindings::RelatedEntityMissingGlobalRegistryId,
75
- "#{self.class.name}(#{id}) has related entities [#{names.join ', '}] missing global_registry_id;" \
76
- ' will retry.'
53
+ "#{model.class.name}(#{model.id}) has related entities [#{names.join ', '}] missing " \
54
+ 'global_registry_id; will retry.'
55
+ end
56
+
57
+ def relationship_entity
58
+ { entity: { global_registry_relationship(type).primary_type => {
59
+ "#{global_registry_relationship(type).related_relationship_name}:relationship" =>
60
+ model.relationship_attributes_to_push(type)
61
+ .merge(global_registry_relationship(type).related_type =>
62
+ global_registry_relationship(type).related_id_value)
63
+ }, client_integration_id: global_registry_relationship(type).primary.id } }
64
+ end
65
+
66
+ def put_relationship_to_global_registry
67
+ GlobalRegistry::Entity.put(
68
+ global_registry_relationship(type).primary_id_value,
69
+ relationship_entity,
70
+ params: {
71
+ full_response: true,
72
+ fields: "#{global_registry_relationship(type).related_relationship_name}:relationship"
73
+ }
74
+ )
75
+ rescue RestClient::BadRequest => e
76
+ response = JSON.parse(e.response.body)
77
+ raise unless response['error'] =~ /^Validation failed:.*already exists$/i
78
+ # Delete relationship entity and retry on 400 Bad Request (client_integration_id already exists)
79
+ delete_relationship_from_global_registry_and_retry
80
+ end
81
+
82
+ def delete_relationship_from_global_registry_and_retry
83
+ GlobalRegistry::Bindings::Workers::DeleteEntityWorker.new.perform(global_registry_relationship(type).id_value)
84
+ model.update_column( # rubocop:disable Rails/SkipsModelValidations
85
+ global_registry_relationship(type).id_column, nil
86
+ )
87
+ raise GlobalRegistry::Bindings::RelatedEntityExistsWithCID,
88
+ "#{model.class.name}(#{model.id}) #{global_registry_relationship(type).related_relationship_name}" \
89
+ ':relationship already exists with client_integration_id(' \
90
+ "#{global_registry_relationship(type).client_integration_id}). Will delete and retry."
77
91
  end
78
92
  end
79
93
  end