cnfs-cognito 0.0.1.alpha1

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +24 -0
  5. data/app/controllers/chown_requests_controller.rb +4 -0
  6. data/app/controllers/chown_results_controller.rb +4 -0
  7. data/app/controllers/cognito/application_controller.rb +6 -0
  8. data/app/controllers/endpoints_controller.rb +4 -0
  9. data/app/controllers/identifiers_controller.rb +4 -0
  10. data/app/controllers/login_controller.rb +45 -0
  11. data/app/controllers/metabase_cards_controller.rb +4 -0
  12. data/app/controllers/metabase_token_controller.rb +46 -0
  13. data/app/controllers/pools_controller.rb +21 -0
  14. data/app/controllers/users_controller.rb +4 -0
  15. data/app/models/chown_request.rb +13 -0
  16. data/app/models/chown_result.rb +14 -0
  17. data/app/models/cognito/application_record.rb +7 -0
  18. data/app/models/endpoint.rb +6 -0
  19. data/app/models/identifier.rb +5 -0
  20. data/app/models/metabase_card.rb +6 -0
  21. data/app/models/platform_event_consumer.rb +8 -0
  22. data/app/models/pool.rb +6 -0
  23. data/app/models/tenant.rb +5 -0
  24. data/app/models/user.rb +50 -0
  25. data/app/models/user_pool.rb +6 -0
  26. data/app/operations/chown_request_process.rb +27 -0
  27. data/app/operations/pool_create.rb +65 -0
  28. data/app/operations/segments/age.rb +65 -0
  29. data/app/operations/segments/birthday.rb +49 -0
  30. data/app/operations/segments/except_ids.rb +28 -0
  31. data/app/operations/segments/gender.rb +35 -0
  32. data/app/operations/segments_apply.rb +40 -0
  33. data/app/policies/chown_request_policy.rb +4 -0
  34. data/app/policies/chown_result_policy.rb +4 -0
  35. data/app/policies/cognito/application_policy.rb +6 -0
  36. data/app/policies/endpoint_policy.rb +4 -0
  37. data/app/policies/identifier_policy.rb +4 -0
  38. data/app/policies/metabase_card_policy.rb +4 -0
  39. data/app/policies/pool_policy.rb +4 -0
  40. data/app/policies/tenant_policy.rb +5 -0
  41. data/app/policies/user_policy.rb +20 -0
  42. data/app/resources/chown_request_resource.rb +5 -0
  43. data/app/resources/chown_result_resource.rb +8 -0
  44. data/app/resources/cognito/application_resource.rb +7 -0
  45. data/app/resources/endpoint_resource.rb +49 -0
  46. data/app/resources/identifier_resource.rb +6 -0
  47. data/app/resources/metabase_card_resource.rb +5 -0
  48. data/app/resources/pool_resource.rb +30 -0
  49. data/app/resources/user_resource.rb +27 -0
  50. data/app/services/metabase/config.rb +16 -0
  51. data/app/services/metabase/token_generator.rb +36 -0
  52. data/config/environment.rb +0 -0
  53. data/config/routes.rb +14 -0
  54. data/config/sidekiq.yml +3 -0
  55. data/config/spring.rb +3 -0
  56. data/db/migrate/20190310013542_create_endpoints.rb +13 -0
  57. data/db/migrate/20190317072927_create_pools.rb +12 -0
  58. data/db/migrate/20190317073055_create_users.rb +18 -0
  59. data/db/migrate/20190317073139_create_identifiers.rb +14 -0
  60. data/db/migrate/20190317073328_create_user_pools.rb +12 -0
  61. data/db/migrate/20191105100356_create_chown_requests.rb +9 -0
  62. data/db/migrate/20191106023315_create_chown_results.rb +12 -0
  63. data/db/migrate/20191126011832_create_metabase_cards.rb +11 -0
  64. data/db/migrate/20191129055605_add_birthday_to_user.rb +5 -0
  65. data/db/migrate/20191205023159_add_gender_to_user.rb +5 -0
  66. data/db/migrate/20191210050613_add_system_generated_flag_to_pools.rb +5 -0
  67. data/db/seeds/development/data.seeds.rb +0 -0
  68. data/db/seeds/development/tenants.seeds.rb +7 -0
  69. data/db/seeds/development/users.seeds.rb +29 -0
  70. data/lib/ros/cognito.rb +16 -0
  71. data/lib/ros/cognito/engine.rb +53 -0
  72. data/lib/ros/cognito/version.rb +7 -0
  73. data/lib/tasks/ros/cognito_tasks.rake +16 -0
  74. 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,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChownRequestPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChownResultPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cognito
4
+ class ApplicationPolicy < ::ApplicationPolicy
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EndpointPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IdentifierPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MetabaseCardPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PoolPolicy < Cognito::ApplicationPolicy
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TenantPolicy < Cognito::ApplicationPolicy
4
+ include Ros::TenantPolicyConcern
5
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChownRequestResource < Cognito::ApplicationResource
4
+ attributes :from_ids, :to_id
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ChownResultResource < Cognito::ApplicationResource
4
+ attributes :from_id, :to_id, :status, :service_name, :chown_request_id
5
+ has_one :chown_request
6
+
7
+ filters :from_id, :to_id, :status
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cognito
4
+ class ApplicationResource < ::ApplicationResource
5
+ abstract
6
+ end
7
+ 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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IdentifierResource < Cognito::ApplicationResource
4
+ attributes :name, :value, :properties
5
+ has_one :user
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MetabaseCardResource < Cognito::ApplicationResource
4
+ attributes :card_id, :identifier
5
+ 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