diff --git a/app/services/attempts_api/redis_client.rb b/app/services/attempts_api/redis_client.rb index 5f9dc2b5e38..efe5f88e303 100644 --- a/app/services/attempts_api/redis_client.rb +++ b/app/services/attempts_api/redis_client.rb @@ -2,18 +2,23 @@ module AttemptsApi class RedisClient + attr_reader :redis_pool + def initialize + @redis_pool = REDIS_ATTEMPTS_API_POOL + end + def write_event(event_key:, jwe:, timestamp:, issuer:) key = key(timestamp, issuer) - REDIS_ATTEMPTS_API_POOL.with do |client| + @redis_pool.with do |client| client.hset(key, event_key, jwe) - client.expire(key, IdentityConfig.store.attempts_api_event_ttl_seconds) + client.expire(key, event_ttl_seconds) end end def read_events(issuer:, batch_size: 1000) events = {} hourly_keys(issuer).each do |hourly_key| - REDIS_ATTEMPTS_API_POOL.with do |client| + @redis_pool.with do |client| client.hscan_each(hourly_key, count: batch_size) do |k, v| break if events.keys.count == batch_size @@ -27,7 +32,7 @@ def read_events(issuer:, batch_size: 1000) def delete_events(issuer:, keys:) total_deleted = 0 hourly_keys(issuer).each do |hourly_key| - REDIS_ATTEMPTS_API_POOL.with do |client| + @redis_pool.with do |client| total_deleted += client.hdel(hourly_key, keys) end end @@ -37,8 +42,12 @@ def delete_events(issuer:, keys:) private + def event_ttl_seconds + IdentityConfig.store.attempts_api_event_ttl_seconds + end + def hourly_keys(issuer) - REDIS_ATTEMPTS_API_POOL.with do |client| + @redis_pool.with do |client| client.keys("attempts-api-events:#{issuer}:*") end.sort end diff --git a/app/services/attempts_api/tracker.rb b/app/services/attempts_api/tracker.rb index bd6caabccf3..dcc984f3de6 100644 --- a/app/services/attempts_api/tracker.rb +++ b/app/services/attempts_api/tracker.rb @@ -24,44 +24,16 @@ def initialize(session_id:, request:, user:, sp:, cookie_device_uuid:, def track_event(event_type, metadata = {}) return unless enabled? - extra_metadata = - if metadata.has_key?(:failure_reason) && - (metadata[:failure_reason].blank? || metadata[:success].present?) - metadata.except(:failure_reason) - else - metadata - end - - event_metadata = { - user_agent: request&.user_agent, - unique_session_id: hashed_session_id, - user_uuid: agency_uuid(event_type: event_type), - device_id: cookie_device_uuid, - user_ip_address: request&.remote_ip, - application_url: sp_redirect_uri, - language: user&.email_language || I18n.locale.to_s, - client_port: CloudFrontHeaderParser.new(request).client_port, - aws_region: IdentityConfig.store.aws_region, - google_analytics_cookies: google_analytics_cookies(request), - } - - event_metadata.merge!(extra_metadata) - event = AttemptEvent.new( event_type: event_type, session_id: session_id, occurred_at: Time.zone.now, - event_metadata: event_metadata, - ) - - jwe = event.to_jwe( - issuer: sp.issuer, - public_key: sp.attempts_public_key, + event_metadata: event_metadata(event_type:, metadata:), ) redis_client.write_event( event_key: event.jti, - jwe: jwe, + jwe: jwe(event), timestamp: event.occurred_at, issuer: sp.issuer, ) @@ -83,6 +55,45 @@ def parse_failure_reason(result) private + def jwe(event) + event.to_jwe( + issuer: sp.issuer, + public_key: sp.attempts_public_key, + ) + end + + def extra_attributes + {} + end + + def extra_metadata(metadata:) + failure_metadata(metadata:).merge(extra_attributes) + end + + def failure_metadata(metadata:) + if metadata.has_key?(:failure_reason) && + (metadata[:failure_reason].blank? || metadata[:success].present?) + metadata.except(:failure_reason) + else + metadata + end + end + + def event_metadata(event_type:, metadata:) + { + user_agent: request&.user_agent, + unique_session_id: hashed_session_id, + user_uuid: agency_uuid(event_type: event_type), + device_id: cookie_device_uuid, + user_ip_address: request&.remote_ip, + application_url: sp_redirect_uri, + language: user&.email_language || I18n.locale.to_s, + client_port: CloudFrontHeaderParser.new(request).client_port, + aws_region: IdentityConfig.store.aws_region, + google_analytics_cookies: google_analytics_cookies(request), + }.merge!(extra_metadata(metadata:)) + end + def google_analytics_cookies(request) return nil unless request&.cookies request.cookies.filter do |key, value|