cdp-sdk 0.14.2
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/lib/cdp/builder.rb +77 -0
- data/lib/cdp/builders/after_created.rb +28 -0
- data/lib/cdp/builders/entity.rb +231 -0
- data/lib/cdp/builders/event.rb +47 -0
- data/lib/cdp/builders/value.rb +56 -0
- data/lib/cdp/client.rb +64 -0
- data/lib/cdp/persistency.rb +48 -0
- data/lib/cdp/query/actions.rb +55 -0
- data/lib/cdp/retryable.rb +27 -0
- data/lib/cdp/timeline/client/swagger/client.rb +124 -0
- data/lib/cdp/timeline/client/swagger/configuration.rb +32 -0
- data/lib/cdp/timeline/event.rb +56 -0
- data/lib/cdp/timeline/response.rb +37 -0
- data/lib/cdp/timeline/summary_response.rb +22 -0
- data/lib/cdp/timeline.rb +47 -0
- data/lib/cdp/transaction.rb +10 -0
- data/lib/cdp/validator.rb +75 -0
- data/lib/cdp/value.rb +98 -0
- data/lib/cdp/version.rb +5 -0
- data/lib/cdp.rb +39 -0
- data/lib/common_pb.rb +30 -0
- data/lib/configuration.rb +5 -0
- data/lib/legacy_pb.rb +49 -0
- data/lib/legacy_services_pb.rb +26 -0
- data/lib/persistency_pb.rb +176 -0
- data/lib/persistency_services_pb.rb +51 -0
- data/lib/search_pb.rb +29 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 97a8b2da1a07af9656615dc77738e79594b7081079bc2f8a78d94b310306b3b2
|
4
|
+
data.tar.gz: 3ded4dda9290e4b64e3f7a0d0ea3c9a3cdf5ed100b88ab4b297a91304f0ae5ef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8aecaabd802e0277a8ccb7191e32e37db63577e616f8e3ef29034495f0bb84ce3145f336608b696e7d301ded2e532d2ff1ec2002b81790b44c341a5c8ca386f2
|
7
|
+
data.tar.gz: 5a0baf5992476d3ab861b3758f02d64b8a04481c69c97622e348145fab9d9c514b41cd7f488981b525100040db77349b4cad0da32fea910fed1ecee81a75e923
|
data/lib/cdp/builder.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDP
|
4
|
+
class Builder
|
5
|
+
def initialize(tenant_id:, schema:)
|
6
|
+
@tenant_id = tenant_id
|
7
|
+
|
8
|
+
filtered_schema = schema.to_hash
|
9
|
+
filtered_schema[:entity_types].each do |entity_type|
|
10
|
+
entity_type[:field_types] = entity_type[:field_types].each_with_object({}) do |field_type, memo|
|
11
|
+
memo[field_type[:name]] = field_type.except(:custom_data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
filtered_schema[:event_types].each do |event_type|
|
16
|
+
event_type[:field_types] = event_type[:field_types].each_with_object({}) do |field_type, memo|
|
17
|
+
memo[field_type[:name]] = field_type.except(:custom_data)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@schema = filtered_schema
|
22
|
+
@builders = generate_builders_map
|
23
|
+
end
|
24
|
+
|
25
|
+
def if_created(source)
|
26
|
+
Builders::AfterCreated.new(self, source)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_transaction
|
30
|
+
CDP::Transaction.new(
|
31
|
+
tenant_id: @tenant_id,
|
32
|
+
commands: []
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(method_name, *_args)
|
37
|
+
method_name_str = method_name.to_s.upcase
|
38
|
+
|
39
|
+
return @builders[method_name_str] if @builders.key?(method_name_str)
|
40
|
+
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# def get_schema!(tenant_id)
|
47
|
+
# context = Microservice::Toolkit::Context.create(tenant_id)
|
48
|
+
# response = CDP::SchemaClient::Client.get_schema(context, tenant_id)
|
49
|
+
|
50
|
+
# raise "Failed to get schema from tenant_id: #{tenant_id}" unless response[:success]
|
51
|
+
|
52
|
+
# response[:schema]
|
53
|
+
# end
|
54
|
+
|
55
|
+
def generate_builders_map
|
56
|
+
builders = {}
|
57
|
+
|
58
|
+
event_types = @schema[:event_types]
|
59
|
+
event_types.each do |event_schema|
|
60
|
+
builders[event_schema[:name]] = Builders::Event.new(event_schema: event_schema, tenant_id: @tenant_id)
|
61
|
+
end
|
62
|
+
|
63
|
+
entity_types = @schema[:entity_types]
|
64
|
+
|
65
|
+
entity_types.each do |entity_schema|
|
66
|
+
entity_relations = @schema[:relation_types].select do |relation|
|
67
|
+
relation[:source_entity_type_name] == entity_schema[:name]
|
68
|
+
end
|
69
|
+
|
70
|
+
builders[entity_schema[:name]] =
|
71
|
+
Builders::Entity.new(entity_schema: entity_schema, tenant_id: @tenant_id, entity_relations: entity_relations)
|
72
|
+
end
|
73
|
+
|
74
|
+
builders
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CDP
|
2
|
+
module Builders
|
3
|
+
class AfterCreated
|
4
|
+
def initialize(builder, source)
|
5
|
+
raise ArgumentError, 'Unsupported multiple commands' if source.commands.size > 1
|
6
|
+
raise ArgumentError, 'Only supported on upsert' if source.commands.first&.upsert_entity.blank?
|
7
|
+
@builder = builder
|
8
|
+
@source = source
|
9
|
+
end
|
10
|
+
|
11
|
+
def relate_to(target)
|
12
|
+
raise ArgumentError, 'Unsupported multiple commands' if target.commands.size > 1
|
13
|
+
raise ArgumentError, 'Supported only create entity' unless target.commands.first&.create_entity.present?
|
14
|
+
|
15
|
+
@source.commands.first.upsert_entity.if_created_relate_with << target.commands.first
|
16
|
+
@source
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_event(target)
|
20
|
+
raise ArgumentError, 'Unsupported multiple commands' if target.commands.size > 1
|
21
|
+
raise ArgumentError, 'Supported only create event' unless target.commands.first&.create_event.present?
|
22
|
+
|
23
|
+
@source.commands.first.upsert_entity.if_created_create_event << target.commands.first
|
24
|
+
@source
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cdp/builders/value'
|
4
|
+
|
5
|
+
module CDP
|
6
|
+
module Builders
|
7
|
+
class Entity
|
8
|
+
include Value
|
9
|
+
|
10
|
+
ID_FIELD = 'entity_uuid'
|
11
|
+
private_constant :ID_FIELD
|
12
|
+
|
13
|
+
def initialize(tenant_id:, entity_schema:, entity_relations:)
|
14
|
+
@tenant_id = tenant_id
|
15
|
+
@entity_schema = entity_schema
|
16
|
+
@entity_relations = entity_relations
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(attributes)
|
20
|
+
validate_attributes_type(attributes)
|
21
|
+
|
22
|
+
validation_result = Validator.new(field_schema: @entity_schema).create(attributes: attributes)
|
23
|
+
|
24
|
+
raise ::CDP::InvalidEntityError, validation_result.errors if validation_result.failed?
|
25
|
+
|
26
|
+
entity_uuid = attributes.delete :entity_uuid
|
27
|
+
create_entity = CDP::CreateEntity.new(
|
28
|
+
entity_type: @entity_schema[:name],
|
29
|
+
entity_uuid: entity_uuid,
|
30
|
+
timestamp: Time.now.utc,
|
31
|
+
payload: build_payload_with_ref(@entity_schema, attributes)
|
32
|
+
)
|
33
|
+
build_execute_request({ create_entity: create_entity })
|
34
|
+
end
|
35
|
+
|
36
|
+
def update(attributes, condition:)
|
37
|
+
validate_condition(condition)
|
38
|
+
validate_attributes_type(attributes)
|
39
|
+
|
40
|
+
validation_result = Validator.new(field_schema: @entity_schema).update(attributes: attributes)
|
41
|
+
raise ::CDP::InvalidEntityError, validation_result.errors if validation_result.failed?
|
42
|
+
|
43
|
+
update_entity = CDP::UpdateEntity.new(
|
44
|
+
entity_type: @entity_schema[:name],
|
45
|
+
timestamp: Time.now.utc,
|
46
|
+
field_condition: build_condition(condition),
|
47
|
+
payload: build_payload(attributes)
|
48
|
+
)
|
49
|
+
build_execute_request({ update_entity: update_entity })
|
50
|
+
end
|
51
|
+
|
52
|
+
def upsert(payload, unique_by:)
|
53
|
+
validate_attributes_type(payload)
|
54
|
+
|
55
|
+
validation_result = Validator.new(field_schema: @entity_schema).upsert(attributes: payload)
|
56
|
+
raise ::CDP::InvalidEntityError, validation_result.errors if validation_result.failed?
|
57
|
+
|
58
|
+
upsert_entity = CDP::UpsertEntity.new(
|
59
|
+
entity_type: @entity_schema[:name],
|
60
|
+
timestamp: Time.now.utc,
|
61
|
+
unique_by: normalize_unique_by(payload, unique_by),
|
62
|
+
payload: build_payload_with_ref(@entity_schema, payload)
|
63
|
+
)
|
64
|
+
build_execute_request({ upsert_entity: upsert_entity })
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_if_not_exists(payload, unique_by:)
|
68
|
+
create_if_not_exists = CDP::CreateIfNotExistsEntity.new(
|
69
|
+
entity_type: @entity_schema[:name],
|
70
|
+
timestamp: Time.now.utc,
|
71
|
+
unique_by: normalize_unique_by(payload, unique_by),
|
72
|
+
payload: build_payload_with_ref(@entity_schema, payload)
|
73
|
+
)
|
74
|
+
build_execute_request({ create_if_not_exists_entity: create_if_not_exists })
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete(condition:)
|
78
|
+
validate_condition(condition)
|
79
|
+
|
80
|
+
delete_entity = CDP::DeleteEntity.new(
|
81
|
+
entity_type: @entity_schema[:name],
|
82
|
+
timestamp: Time.now.utc,
|
83
|
+
field_condition: build_condition(condition)
|
84
|
+
)
|
85
|
+
build_execute_request({ delete_entity: delete_entity })
|
86
|
+
end
|
87
|
+
|
88
|
+
def append_set_values(condition:, field_name:, values:, timestamp: Time.now.utc)
|
89
|
+
validate_condition(condition)
|
90
|
+
validate_set_field(field_name)
|
91
|
+
|
92
|
+
append_values = CDP::AppendValuesToEntitySet.new(
|
93
|
+
entity_type: @entity_schema[:name],
|
94
|
+
timestamp: timestamp,
|
95
|
+
field_condition: build_condition(condition),
|
96
|
+
field_name: field_name,
|
97
|
+
values: build(@entity_schema, field_name, values).array_value
|
98
|
+
)
|
99
|
+
build_execute_request({ append_values_to_entity_set: append_values })
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete_set_values(condition:, field_name:, values:, timestamp: Time.now.utc)
|
103
|
+
validate_condition(condition)
|
104
|
+
validate_set_field(field_name)
|
105
|
+
|
106
|
+
delete_values = CDP::DeleteValuesFromEntitySet.new(
|
107
|
+
entity_type: @entity_schema[:name],
|
108
|
+
timestamp: timestamp,
|
109
|
+
field_condition: build_condition(condition),
|
110
|
+
field_name: field_name,
|
111
|
+
values: build(@entity_schema, field_name, values).array_value
|
112
|
+
)
|
113
|
+
build_execute_request({ delete_values_from_entity_set: delete_values })
|
114
|
+
end
|
115
|
+
|
116
|
+
def method_missing(method_name, args)
|
117
|
+
method_name_str = method_name.to_s.downcase
|
118
|
+
|
119
|
+
builders = entity_relations_builders
|
120
|
+
if builders.include?(method_name_str)
|
121
|
+
source_id = args.fetch(:source_id)
|
122
|
+
target_id = args.fetch(:target_id)
|
123
|
+
return builders[method_name_str].call(source_id, target_id)
|
124
|
+
end
|
125
|
+
|
126
|
+
super
|
127
|
+
end
|
128
|
+
|
129
|
+
def exists?(_condition)
|
130
|
+
raise NoMethodError, "The 'exists?' for entities is not implemented"
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def normalize_unique_by(payload, unnormalized_unique_by)
|
136
|
+
unique_by = Array(unnormalized_unique_by).compact.map(&:to_sym)
|
137
|
+
raise ArgumentError, 'Field unique by empty' if unique_by.empty?
|
138
|
+
|
139
|
+
payload_keys = payload.symbolize_keys.keys
|
140
|
+
raise ArgumentError, 'Field unique by is required in payload' unless unique_by.all? do |field|
|
141
|
+
payload_keys.include?(field)
|
142
|
+
end
|
143
|
+
|
144
|
+
unique_by
|
145
|
+
end
|
146
|
+
|
147
|
+
def validate_attributes_type(attributes)
|
148
|
+
raise ArgumentError, 'attributes should be a Hash' unless attributes.is_a?(Hash)
|
149
|
+
end
|
150
|
+
|
151
|
+
def validate_condition(condition)
|
152
|
+
raise ArgumentError, 'condition should be a Hash' unless condition.is_a?(Hash)
|
153
|
+
|
154
|
+
is_correlator = Validator.new(field_schema: @entity_schema).is_correlator?(field_name: condition[:field_name])
|
155
|
+
raise InvalidCorrelatorError, "Field: #{condition[:field_name]} is not a correlator." unless is_correlator
|
156
|
+
end
|
157
|
+
|
158
|
+
def validate_set_field(field_name)
|
159
|
+
field_type = @entity_schema[:field_types][field_name.to_s]
|
160
|
+
raise CDP::SchemaMissingFieldError, "invalid field #{field_name}, is not on schema" unless field_type
|
161
|
+
|
162
|
+
unless field_type[:data_type] == 'SET'
|
163
|
+
raise CDP::InvalidFieldTypeError,
|
164
|
+
"invalid field #{field_name} type, is not a SET"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def entity_relations_builders
|
169
|
+
@entity_relations.each_with_object({}) do |relation, memo|
|
170
|
+
target_type = relation[:target_entity_type_name]
|
171
|
+
memo["add_#{target_type.downcase}_relation"] = lambda { |source_id, target_id|
|
172
|
+
add_relation(target_type, source_id, target_id)
|
173
|
+
}
|
174
|
+
|
175
|
+
memo["remove_#{target_type.downcase}_relation"] = lambda { |source_id, target_id|
|
176
|
+
remove_relation(target_type, source_id, target_id)
|
177
|
+
}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_relation(target_entity_type, source_id, target_id)
|
182
|
+
create_entity_relation = CDP::CreateEntityRelation.new(
|
183
|
+
source_entity_type: @entity_schema[:name],
|
184
|
+
source_entity_id: source_id,
|
185
|
+
target_entity_type: target_entity_type,
|
186
|
+
target_entity_id: target_id,
|
187
|
+
timestamp: Time.now.utc
|
188
|
+
)
|
189
|
+
build_execute_request({ create_entity_relation: create_entity_relation })
|
190
|
+
end
|
191
|
+
|
192
|
+
def remove_relation(target_entity_type, source_id, target_id)
|
193
|
+
delete_entity_relation = CDP::DeleteEntityRelation.new(
|
194
|
+
source_entity_type: @entity_schema[:name],
|
195
|
+
source_entity_id: source_id,
|
196
|
+
target_entity_type: target_entity_type,
|
197
|
+
target_entity_id: target_id,
|
198
|
+
timestamp: Time.now.utc
|
199
|
+
)
|
200
|
+
build_execute_request({ delete_entity_relation: delete_entity_relation })
|
201
|
+
end
|
202
|
+
|
203
|
+
def build_execute_request(command_payload)
|
204
|
+
CDP::Transaction.new(
|
205
|
+
tenant_id: @tenant_id,
|
206
|
+
commands: [CDP::Command.new(command_payload.merge(id: SecureRandom.uuid))]
|
207
|
+
)
|
208
|
+
end
|
209
|
+
|
210
|
+
def build_condition(condition)
|
211
|
+
field_name, field_value = condition.values
|
212
|
+
field_value = if ID_FIELD == field_name
|
213
|
+
CDP::Value.create_string(field_value)
|
214
|
+
else
|
215
|
+
build(@entity_schema, field_name, field_value)
|
216
|
+
end
|
217
|
+
|
218
|
+
CDP::FieldCondition.new(
|
219
|
+
field_name: field_name,
|
220
|
+
field_value: field_value
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
def build_payload(attributes)
|
225
|
+
attributes.each_with_object({}) do |(name, value), memo|
|
226
|
+
memo[name] = build(@entity_schema, name, value)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cdp/builders/value'
|
4
|
+
|
5
|
+
module CDP
|
6
|
+
module Builders
|
7
|
+
class Event
|
8
|
+
include Value
|
9
|
+
|
10
|
+
def initialize(tenant_id:, event_schema:)
|
11
|
+
@tenant_id = tenant_id
|
12
|
+
@event_schema = event_schema
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(event)
|
16
|
+
event = event.symbolize_keys
|
17
|
+
|
18
|
+
raise ArgumentError, 'attribute event_identifier is missing' unless event.key?(:event_identifier)
|
19
|
+
|
20
|
+
raise ArgumentError, 'attribute event_identifier is empty' if event[:event_identifier].empty?
|
21
|
+
|
22
|
+
payload = event.except(:event_uuid, :event_identifier, :event_timestamp, :entity_uuid)
|
23
|
+
|
24
|
+
validation_result = Validator.new(field_schema: @event_schema).create(attributes: payload)
|
25
|
+
|
26
|
+
raise ::CDP::InvalidEntityError, validation_result.errors if validation_result.failed?
|
27
|
+
|
28
|
+
CDP::Transaction.new(
|
29
|
+
tenant_id: @tenant_id,
|
30
|
+
commands: [
|
31
|
+
CDP::Command.new(
|
32
|
+
id: SecureRandom.uuid,
|
33
|
+
create_event: CDP::CreateEvent.new(
|
34
|
+
event_type: @event_schema[:name],
|
35
|
+
event_uuid: event[:event_uuid] || SecureRandom.uuid,
|
36
|
+
event_identifier: event[:event_identifier],
|
37
|
+
event_timestamp: event[:event_timestamp] || Time.now.utc,
|
38
|
+
entity_uuid: event[:entity_uuid],
|
39
|
+
payload: build_payload_with_ref(@event_schema, payload)
|
40
|
+
)
|
41
|
+
)
|
42
|
+
]
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDP
|
4
|
+
module Builders
|
5
|
+
module Value
|
6
|
+
module_function
|
7
|
+
|
8
|
+
VALID_COMMAND_TYPE = %i[upsert_entity create_entity update_entity create_if_not_exists_entity].freeze
|
9
|
+
|
10
|
+
def build_payload_with_ref(schema, attributes, unsupported_types=[])
|
11
|
+
attributes.each_with_object({}) do |(name, value), memo|
|
12
|
+
if value.class == CDP::Transaction
|
13
|
+
raise ArgumentError, 'Unsupported multiple commands' if value.commands.size > 1
|
14
|
+
raise ArgumentError, 'Unsupported command' unless supported_command(value.commands.first)
|
15
|
+
|
16
|
+
memo[name] = CDP::Value.new(
|
17
|
+
reference: CDP::CommandReference.new(id: value.commands.first.id)
|
18
|
+
)
|
19
|
+
next
|
20
|
+
end
|
21
|
+
memo[name] = Value.build(schema, name, value, unsupported_types)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def supported_command(command)
|
26
|
+
VALID_COMMAND_TYPE.include?(command.command_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def build(schema, field_name, field_value, unsupported_types=[])
|
30
|
+
field_type = schema[:field_types][field_name.to_s]
|
31
|
+
|
32
|
+
raise CDP::SchemaMissingFieldError, "invalid field #{field_name}, is not on schema" unless field_type
|
33
|
+
raise ArgumentError, "unsupported type #{field_type[:data_type]} (#{field_type[:name]})" if unsupported_types.include?(field_type[:data_type])
|
34
|
+
|
35
|
+
case field_type[:data_type]
|
36
|
+
when 'STRING'
|
37
|
+
CDP::Value.create_string(field_value)
|
38
|
+
when 'TEXT'
|
39
|
+
CDP::Value.create_text(field_value)
|
40
|
+
when 'BOOLEAN'
|
41
|
+
CDP::Value.create_bool(field_value)
|
42
|
+
when 'INTEGER'
|
43
|
+
CDP::Value.create_int(field_value)
|
44
|
+
when 'FLOAT'
|
45
|
+
CDP::Value.create_float(field_value)
|
46
|
+
when 'TIMESTAMP'
|
47
|
+
CDP::Value.create_timestamp(field_value)
|
48
|
+
when 'SET'
|
49
|
+
CDP::Value.create_set_values(field_value)
|
50
|
+
else
|
51
|
+
raise "invalid value type: #{field_type[:name]} (#{field_type[:data_type]}) = '#{field_value}'"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/cdp/client.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../cdp/retryable'
|
2
|
+
require_relative '../configuration'
|
3
|
+
|
4
|
+
module CDP
|
5
|
+
class Client
|
6
|
+
include CDP::Retryable
|
7
|
+
include Configuration
|
8
|
+
attr_reader :legacy
|
9
|
+
|
10
|
+
AVAILABLE_PRIORITIES = %i[low normal high]
|
11
|
+
|
12
|
+
def initialize(service_name:, priority: :normal, url: CDP_CORE_SERVICE_URL, secure: :this_channel_is_insecure, retries: 3)
|
13
|
+
validate_priorities(priority)
|
14
|
+
@service_name = service_name
|
15
|
+
@priority = priority
|
16
|
+
@retries = retries
|
17
|
+
@service = CDP::Service::Stub.new(url, secure)
|
18
|
+
@search_service = CDP::SearchService::Stub.new(url, secure)
|
19
|
+
@legacy = CDP::Legacy::Service::Stub.new(url, secure)
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute_sync(transaction, priority: @priority)
|
23
|
+
validate_priorities(priority)
|
24
|
+
response = retry_block(limit: @retries) {
|
25
|
+
@service.execute_sync(transaction, metadata(priority: priority))
|
26
|
+
}
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute_async(transaction, priority: @priority)
|
31
|
+
validate_priorities(priority)
|
32
|
+
response = retry_block(limit: @retries) {
|
33
|
+
@service.execute_async(transaction, metadata(priority: priority))
|
34
|
+
}
|
35
|
+
response
|
36
|
+
end
|
37
|
+
|
38
|
+
def search(request:)
|
39
|
+
retry_block(limit: @retries) {
|
40
|
+
@search_service.execute(request)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def method_missing(method_name, *_args)
|
45
|
+
Query::Actions.new(self, method_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def validate_priorities(priority)
|
51
|
+
raise ArgumentError, 'invalid priority' unless AVAILABLE_PRIORITIES.include?(priority)
|
52
|
+
end
|
53
|
+
|
54
|
+
def metadata(priority:)
|
55
|
+
{
|
56
|
+
metadata: {
|
57
|
+
service_name: @service_name,
|
58
|
+
priority: priority.to_s,
|
59
|
+
version: CDP::VERSION
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative '../cdp/retryable'
|
2
|
+
require_relative '../configuration'
|
3
|
+
|
4
|
+
module CDP
|
5
|
+
class Persistency
|
6
|
+
class << self
|
7
|
+
include CDP::Retryable
|
8
|
+
include Configuration
|
9
|
+
|
10
|
+
def new(tenant_id:)
|
11
|
+
schema = get_schema(tenant_id)
|
12
|
+
CDP::Builder.new(tenant_id: tenant_id, schema: schema)
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(requests:, retries: 3)
|
16
|
+
raise ArgumentError, 'requests should be an Array' unless requests.is_a?(Array)
|
17
|
+
|
18
|
+
response = retry_block(limit: retries) {
|
19
|
+
client.execute(requests)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def client
|
24
|
+
@client ||= default_client
|
25
|
+
end
|
26
|
+
|
27
|
+
def client=(client)
|
28
|
+
@client = client
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def default_client
|
34
|
+
CDP::PersistencyService::Stub.new(CDP_CORE_SERVICE_URL, :this_channel_is_insecure)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_schema(tenant_id)
|
38
|
+
ctx = Mstk::Context.create(tenant_id)
|
39
|
+
response = CDP::SchemaClient::Client.get_schema(ctx, tenant_id)
|
40
|
+
success, error, schema = response.values_at(:success, :error, :schema)
|
41
|
+
|
42
|
+
raise "Could not get schema for tenant '#{@tenant_id}': '#{error}'" unless success
|
43
|
+
|
44
|
+
schema
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDP
|
4
|
+
module Query
|
5
|
+
class Actions
|
6
|
+
def initialize(service, object_type)
|
7
|
+
@service = service
|
8
|
+
@object_type = object_type.to_s.upcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def exists?(tenant_id:, condition:)
|
12
|
+
request = CDP::SearchRequest.new(
|
13
|
+
tenant_id: tenant_id,
|
14
|
+
exists: CDP::Exists.new(
|
15
|
+
type: @object_type,
|
16
|
+
field_condition: build_condition(condition)
|
17
|
+
)
|
18
|
+
)
|
19
|
+
|
20
|
+
@service.search(request: request)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def distinct(tenant_id:, fields:, condition:)
|
25
|
+
request = CDP::SearchRequest.new(
|
26
|
+
tenant_id: tenant_id,
|
27
|
+
distinct: CDP::Distinct.new(
|
28
|
+
type: @object_type,
|
29
|
+
fields: Array(fields),
|
30
|
+
field_condition: build_condition(condition)
|
31
|
+
)
|
32
|
+
)
|
33
|
+
|
34
|
+
@service.search(request: request)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def build_condition(condition)
|
40
|
+
raise ArgumentError, 'condition should be a Hash' unless condition.is_a?(Hash)
|
41
|
+
|
42
|
+
raise ArgumentError, 'condition should has one value' unless condition.length != 0
|
43
|
+
|
44
|
+
field_name, field_value = condition.first
|
45
|
+
|
46
|
+
field_value = Array(field_value)
|
47
|
+
|
48
|
+
CDP::FieldCondition.new(
|
49
|
+
field_name: field_name,
|
50
|
+
field_value: CDP::Value.create_set_values(field_value)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'grpc'
|
2
|
+
|
3
|
+
module CDP
|
4
|
+
module Retryable
|
5
|
+
DEFAULT_CDP_CLIENT_RETRYABLE_ERRORS = 'Errno::ECONNRESET,Errno::ECONNREFUSED,GRPC::ResourceExhausted,GRPC::Unavailable'
|
6
|
+
|
7
|
+
CDP_CLIENT_RETRYABLE_ERRORS = [
|
8
|
+
ENV.fetch('CDP_CLIENT_RETRYABLE_ERRORS', DEFAULT_CDP_CLIENT_RETRYABLE_ERRORS).split(',')
|
9
|
+
].flatten
|
10
|
+
|
11
|
+
def retry_block(limit: 3, backoff: 1)
|
12
|
+
tries = 0
|
13
|
+
|
14
|
+
begin
|
15
|
+
yield
|
16
|
+
rescue => e
|
17
|
+
if CDP_CLIENT_RETRYABLE_ERRORS.include?(e.class.to_s) && tries < limit
|
18
|
+
sleep backoff * tries
|
19
|
+
tries += 1
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|