cnfs-cognito 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +24 -0
- data/app/controllers/chown_requests_controller.rb +4 -0
- data/app/controllers/chown_results_controller.rb +4 -0
- data/app/controllers/cognito/application_controller.rb +6 -0
- data/app/controllers/endpoints_controller.rb +4 -0
- data/app/controllers/identifiers_controller.rb +4 -0
- data/app/controllers/login_controller.rb +45 -0
- data/app/controllers/metabase_cards_controller.rb +4 -0
- data/app/controllers/metabase_token_controller.rb +46 -0
- data/app/controllers/pools_controller.rb +21 -0
- data/app/controllers/users_controller.rb +4 -0
- data/app/models/chown_request.rb +13 -0
- data/app/models/chown_result.rb +14 -0
- data/app/models/cognito/application_record.rb +7 -0
- data/app/models/endpoint.rb +6 -0
- data/app/models/identifier.rb +5 -0
- data/app/models/metabase_card.rb +6 -0
- data/app/models/platform_event_consumer.rb +8 -0
- data/app/models/pool.rb +6 -0
- data/app/models/tenant.rb +5 -0
- data/app/models/user.rb +50 -0
- data/app/models/user_pool.rb +6 -0
- data/app/operations/chown_request_process.rb +27 -0
- data/app/operations/pool_create.rb +65 -0
- data/app/operations/segments/age.rb +65 -0
- data/app/operations/segments/birthday.rb +49 -0
- data/app/operations/segments/except_ids.rb +28 -0
- data/app/operations/segments/gender.rb +35 -0
- data/app/operations/segments_apply.rb +40 -0
- data/app/policies/chown_request_policy.rb +4 -0
- data/app/policies/chown_result_policy.rb +4 -0
- data/app/policies/cognito/application_policy.rb +6 -0
- data/app/policies/endpoint_policy.rb +4 -0
- data/app/policies/identifier_policy.rb +4 -0
- data/app/policies/metabase_card_policy.rb +4 -0
- data/app/policies/pool_policy.rb +4 -0
- data/app/policies/tenant_policy.rb +5 -0
- data/app/policies/user_policy.rb +20 -0
- data/app/resources/chown_request_resource.rb +5 -0
- data/app/resources/chown_result_resource.rb +8 -0
- data/app/resources/cognito/application_resource.rb +7 -0
- data/app/resources/endpoint_resource.rb +49 -0
- data/app/resources/identifier_resource.rb +6 -0
- data/app/resources/metabase_card_resource.rb +5 -0
- data/app/resources/pool_resource.rb +30 -0
- data/app/resources/user_resource.rb +27 -0
- data/app/services/metabase/config.rb +16 -0
- data/app/services/metabase/token_generator.rb +36 -0
- data/config/environment.rb +0 -0
- data/config/routes.rb +14 -0
- data/config/sidekiq.yml +3 -0
- data/config/spring.rb +3 -0
- data/db/migrate/20190310013542_create_endpoints.rb +13 -0
- data/db/migrate/20190317072927_create_pools.rb +12 -0
- data/db/migrate/20190317073055_create_users.rb +18 -0
- data/db/migrate/20190317073139_create_identifiers.rb +14 -0
- data/db/migrate/20190317073328_create_user_pools.rb +12 -0
- data/db/migrate/20191105100356_create_chown_requests.rb +9 -0
- data/db/migrate/20191106023315_create_chown_results.rb +12 -0
- data/db/migrate/20191126011832_create_metabase_cards.rb +11 -0
- data/db/migrate/20191129055605_add_birthday_to_user.rb +5 -0
- data/db/migrate/20191205023159_add_gender_to_user.rb +5 -0
- data/db/migrate/20191210050613_add_system_generated_flag_to_pools.rb +5 -0
- data/db/seeds/development/data.seeds.rb +0 -0
- data/db/seeds/development/tenants.seeds.rb +7 -0
- data/db/seeds/development/users.seeds.rb +29 -0
- data/lib/ros/cognito.rb +16 -0
- data/lib/ros/cognito/engine.rb +53 -0
- data/lib/ros/cognito/version.rb +7 -0
- data/lib/tasks/ros/cognito_tasks.rake +16 -0
- metadata +162 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Segments
|
4
|
+
class Age < Ros::ActivityBase
|
5
|
+
step :init
|
6
|
+
step :verify_segment
|
7
|
+
failed :incorrect_segment, Output(:success) => End(:failure)
|
8
|
+
step :translate_segment
|
9
|
+
step :apply_segment
|
10
|
+
failed :segment_not_applied, Output(:success) => End(:failure)
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def init(ctx, segment:, **)
|
15
|
+
ctx[:segment] = Array.wrap(segment)
|
16
|
+
ctx[:translated_segments] = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify_segment(_ctx, segment:, **)
|
20
|
+
segment.all? { |s| segment_element_valid?(s) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def incorrect_segment(_ctx, errors:, segment:, **)
|
24
|
+
errors.add(:segment, "Incorrect age segment value: #{segment}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def translate_segment(_ctx, segment:, translated_segments:, **)
|
28
|
+
segment.each do |element|
|
29
|
+
translated_segments << case element
|
30
|
+
when Hash
|
31
|
+
el = element.with_indifferent_access
|
32
|
+
date_range(el['from'], el['to'])
|
33
|
+
when String, Integer
|
34
|
+
date_range(element, element)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply_segment(ctx, users:, translated_segments:, **)
|
40
|
+
ctx[:model] = users.where(birthday: translated_segments)
|
41
|
+
end
|
42
|
+
|
43
|
+
def segment_not_applied(_ctx, errors:, segment:, **)
|
44
|
+
errors.add(:model, "can't apply age segment: #{segment}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def segment_element_valid?(element)
|
48
|
+
case element
|
49
|
+
when Hash
|
50
|
+
element.with_indifferent_access.keys.sort == %w[from to] && element.values.all? { |el| el.to_s.match(/^\d+$/) }
|
51
|
+
when String, Integer
|
52
|
+
element.to_s.match(/^\d+$/)
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def date_range(from, to, base_date = Time.zone.today)
|
59
|
+
Range.new(
|
60
|
+
base_date - to.to_i.years - 1.year + 1.day,
|
61
|
+
base_date - from.to_i.years
|
62
|
+
)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Segments
|
4
|
+
class Birthday < Ros::ActivityBase
|
5
|
+
step :init
|
6
|
+
step :verify_segment
|
7
|
+
failed :incorrect_segment, Output(:success) => End(:failure)
|
8
|
+
step :apply_segment
|
9
|
+
failed :segment_not_applied, Output(:success) => End(:failure)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def init(ctx, segment:, **)
|
14
|
+
if segment.match(/^\d{2}$/)
|
15
|
+
ctx[:date] = Date.strptime(segment, '%m')
|
16
|
+
ctx[:segment] = 'this_month'
|
17
|
+
elsif segment.match(/^\d{2}-\d{2}$/)
|
18
|
+
ctx[:date] = Date.strptime(segment, '%m-%d')
|
19
|
+
ctx[:segment] = 'this_day'
|
20
|
+
else
|
21
|
+
ctx[:date] = Time.zone.today
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify_segment(_ctx, segment:, **)
|
26
|
+
%w[this_day this_month].include?(segment)
|
27
|
+
end
|
28
|
+
|
29
|
+
def incorrect_segment(_ctx, errors:, segment:, **)
|
30
|
+
errors.add(:segment, "Incorrect birthday segment value: #{segment}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply_segment(ctx, users:, segment:, date:, **)
|
34
|
+
ctx[:model] = send(segment, users, date)
|
35
|
+
end
|
36
|
+
|
37
|
+
def segment_not_applied(_ctx, errors:, segment:, **)
|
38
|
+
errors.add(:model, "Can't apply birthday segment: #{segment}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def this_day(users, date)
|
42
|
+
users.where("TO_CHAR(birthday, 'DD-MM') = TO_CHAR(DATE(?), 'DD-MM')", date)
|
43
|
+
end
|
44
|
+
|
45
|
+
def this_month(users, date)
|
46
|
+
users.where("TO_CHAR(birthday, 'MM') = TO_CHAR(DATE(?), 'MM')", date)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Segments
|
4
|
+
class ExceptIds < Ros::ActivityBase
|
5
|
+
step :verify_segment
|
6
|
+
failed :incorrect_segment, Output(:success) => End(:failure)
|
7
|
+
step :apply_segment
|
8
|
+
failed :segment_not_applied
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def verify_segment(_ctx, segment:, **)
|
13
|
+
segment.is_a?(Array)
|
14
|
+
end
|
15
|
+
|
16
|
+
def incorrect_segment(_ctx, errors:, segment:, **)
|
17
|
+
errors.add(:segment, "Incorrect except ids segment value: #{segment}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def apply_segment(ctx, users:, segment:, **)
|
21
|
+
ctx[:model] = users.where.not(id: segment)
|
22
|
+
end
|
23
|
+
|
24
|
+
def segment_not_applied(_ctx, errors:, segment:, **)
|
25
|
+
errors.add(:model, "Can't apply except ids segment: #{segment}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Segments
|
4
|
+
class Gender < Ros::ActivityBase
|
5
|
+
step :verify_segment
|
6
|
+
failed :incorrect_segment, Output(:success) => End(:failure)
|
7
|
+
step :apply_segment
|
8
|
+
failed :segment_not_applied, Output(:success) => End(:failure)
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def verify_segment(_ctx, segment:, **)
|
13
|
+
case segment
|
14
|
+
when Array
|
15
|
+
(segment - %w[male female other]).empty?
|
16
|
+
when String
|
17
|
+
%w[male female other any].include?(segment)
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def incorrect_segment(_ctx, errors:, segment:, **)
|
24
|
+
errors.add(:segment, "Incorrect gender segment value: #{segment}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def apply_segment(ctx, users:, segment:, **)
|
28
|
+
ctx[:model] = segment == 'any' ? users : users.where(gender: segment)
|
29
|
+
end
|
30
|
+
|
31
|
+
def segment_not_applied(_ctx, errors:, segment:, **)
|
32
|
+
errors.add(:model, "Can't apply gender segment: #{segment}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SegmentsApply < Ros::ActivityBase
|
4
|
+
step :init
|
5
|
+
step :apply_segments
|
6
|
+
failed :unset_model
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def init(ctx, users:, **)
|
11
|
+
ctx[:model] = users
|
12
|
+
end
|
13
|
+
|
14
|
+
def apply_segments(ctx, model:, segments:, errors:, **)
|
15
|
+
segments.each do |segment_key, segment_value|
|
16
|
+
res = apply_segment(segment_key, segment_value, model, errors)
|
17
|
+
return false if res.nil?
|
18
|
+
|
19
|
+
if res.failure?
|
20
|
+
ctx[:errors] = res.errors
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
model = res.model
|
25
|
+
end
|
26
|
+
ctx[:model] = model
|
27
|
+
end
|
28
|
+
|
29
|
+
def apply_segment(segment_key, segment_value, users, errors)
|
30
|
+
segment_class = "Segments::#{segment_key.classify}".constantize
|
31
|
+
segment_class.call(users: users, segment: segment_value)
|
32
|
+
rescue NameError => _e
|
33
|
+
errors.add(:segment, "can't find segmentation class")
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def unset_model(ctx, **)
|
38
|
+
ctx[:model] = nil
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserPolicy < Cognito::ApplicationPolicy
|
4
|
+
class Scope
|
5
|
+
attr_reader :user, :scope
|
6
|
+
|
7
|
+
def initialize(user, scope)
|
8
|
+
@user = user
|
9
|
+
@scope = scope
|
10
|
+
end
|
11
|
+
|
12
|
+
def resolve
|
13
|
+
if user.cognito_user_id
|
14
|
+
scope.where(id: user.cognito_user_id)
|
15
|
+
else
|
16
|
+
scope.all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EndpointResource < Cognito::ApplicationResource
|
4
|
+
attributes :url, :properties, :target_type, :target_id
|
5
|
+
|
6
|
+
filters :target_type, :target_id
|
7
|
+
filter :url
|
8
|
+
|
9
|
+
def custom_links(options)
|
10
|
+
uri = URI(options[:serializer].link_builder.self_link(self))
|
11
|
+
{ target: target_uri(uri) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def target_uri(uri)
|
15
|
+
return '' unless @model.target_type
|
16
|
+
|
17
|
+
service, model = @model.target_type.underscore.split('/')
|
18
|
+
case Settings.external_connection.type
|
19
|
+
when 'service'
|
20
|
+
uri.host = uri.host.gsub(Settings.service.name, service)
|
21
|
+
uri.path = ['', model, @model.target_id].join('/')
|
22
|
+
when 'path'
|
23
|
+
uri.path = ['', service, model, @model.target_id].join('/')
|
24
|
+
when 'port'
|
25
|
+
# TODO: Test for whether service_endpoint is nil
|
26
|
+
uri.port = URI(Ros::Sdk.service_endpoints[service]).port
|
27
|
+
when 'host'
|
28
|
+
uri.host = URI(Ros::Sdk.service_endpoints[service]).host
|
29
|
+
end
|
30
|
+
uri.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetchable_fields
|
34
|
+
super - [:urn]
|
35
|
+
end
|
36
|
+
|
37
|
+
# def self.updatable_fields(context)
|
38
|
+
# super - [:full_name]
|
39
|
+
# end
|
40
|
+
|
41
|
+
# def self.creatable_fields(context)
|
42
|
+
# super - [:full_name]
|
43
|
+
# end
|
44
|
+
# def resource; [@model.target_type.underscore.split('/').last, @model.target_id].join('/') end
|
45
|
+
|
46
|
+
# def self.sortable_fields(context)
|
47
|
+
# super(context) - [:body]
|
48
|
+
# end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PoolResource < Cognito::ApplicationResource
|
4
|
+
attributes :name, :properties, :user_count, :system_generated
|
5
|
+
has_many :users
|
6
|
+
|
7
|
+
filter :query, apply: lambda { |records, value, _options|
|
8
|
+
query_by_id = 'pools.id IN (:id_query)'
|
9
|
+
query_by_non_id_attrs = %w[name]
|
10
|
+
.map { |field| "#{field} ILIKE :ilike_query" }
|
11
|
+
.join(' OR ')
|
12
|
+
|
13
|
+
filter_fields = /^\d+$/.match?(value[0]) ? query_by_id : query_by_non_id_attrs
|
14
|
+
records.where(filter_fields, id_query: value[0], ilike_query: "%#{value[0]}%")
|
15
|
+
}
|
16
|
+
|
17
|
+
filter :system_generated
|
18
|
+
|
19
|
+
def self.updatable_fields(context)
|
20
|
+
super - %i[user_count]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.creatable_fields(context)
|
24
|
+
super - %i[user_count]
|
25
|
+
end
|
26
|
+
|
27
|
+
def user_count
|
28
|
+
users.size
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserResource < Cognito::ApplicationResource
|
4
|
+
attributes :title, :first_name, :last_name, :phone_number, :email_address,
|
5
|
+
:primary_identifier, :properties, :anonymous, :birthday, :gender
|
6
|
+
filter :primary_identifier
|
7
|
+
|
8
|
+
filter :query, apply: lambda { |records, value, _options|
|
9
|
+
searchable_columns = ['primary_identifier', 'first_name', 'last_name', 'phone_number', 'email_address',
|
10
|
+
"first_name || ' ' || last_name", "last_name || ' ' || first_name"]
|
11
|
+
query_by_non_id_attrs = searchable_columns.map { |field| "#{field} ILIKE :non_id_query" }.join(' OR ')
|
12
|
+
|
13
|
+
results = records.where(query_by_non_id_attrs, non_id_query: "%#{value[0]}%")
|
14
|
+
results = results.or(records.where(id: value[0])) if /^(\d)+$/.match?(value[0])
|
15
|
+
results
|
16
|
+
}
|
17
|
+
|
18
|
+
filter :segments, apply: lambda { |records, value, _options|
|
19
|
+
SegmentsApply.call(users: records, segments: value[0].permit!.to_h).model
|
20
|
+
}
|
21
|
+
|
22
|
+
filter :pool_id, apply: lambda { |records, value, _options|
|
23
|
+
records.joins(:pools).where(pools: { id: value })
|
24
|
+
}
|
25
|
+
|
26
|
+
has_many :pools
|
27
|
+
end
|