global-registry-bindings 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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