Class: Keycardai::OAuth::AccessContext

Inherits:
Object
  • Object
show all
Defined in:
lib/keycardai/oauth/access_context.rb

Overview

The per-request container of exchanged downstream tokens, keyed by resource, with per-resource error tracking. The grant layer constructs and populates it before the handler runs; handlers read it from the request context. Population is non-throwing by design: failures land on the context so a partial-success flow can proceed when only some exchanges failed.

Reading is split between the throwing accessor #access and the non-throwing getters (#resource_error, #error, #errors?, #status, ...), so handlers can inspect state and choose to proceed, degrade, or fail.

Instance Method Summary collapse

Constructor Details

#initialize(tokens = {}) ⇒ AccessContext

Returns a new instance of AccessContext.

Parameters:

  • tokens (Hash{String => TokenResponse}) (defaults to: {})

    seed of successful exchanges



17
18
19
20
21
22
# File 'lib/keycardai/oauth/access_context.rb', line 17

def initialize(tokens = {})
  @tokens = tokens.dup
  @resource_errors = {}
  @error = nil
  @mutex = Mutex.new
end

Instance Method Details

#access(resource) ⇒ TokenResponse

The exchanged token for a resource.

Parameters:

  • resource (String)

Returns:

Raises:

  • (ResourceAccessError)

    a global error is set (global_error), the resource recorded an error (resource_error), or no token exists for it (missing_token)



31
32
33
34
35
36
37
38
39
40
# File 'lib/keycardai/oauth/access_context.rb', line 31

def access(resource)
  @mutex.synchronize do
    raise_access_error("global_error", resource, details: @error) if @error
    if @resource_errors.key?(resource)
      raise_access_error("resource_error", resource, details: @resource_errors[resource])
    end

    @tokens[resource] || raise_access_error("missing_token", resource, available: @tokens.keys)
  end
end

#errorObject?

Returns the global (context-wide) error.

Returns:

  • (Object, nil)

    the global (context-wide) error



49
50
51
# File 'lib/keycardai/oauth/access_context.rb', line 49

def error
  @mutex.synchronize { @error }
end

#error?Boolean

Returns whether a global error was set.

Returns:

  • (Boolean)

    whether a global error was set



65
66
67
# File 'lib/keycardai/oauth/access_context.rb', line 65

def error?
  @mutex.synchronize { !@error.nil? }
end

#errorsHash

Returns { resources: Hash=> Object, error: Object | nil }.

Returns:

  • (Hash)

    { resources: Hash=> Object, error: Object | nil }



54
55
56
# File 'lib/keycardai/oauth/access_context.rb', line 54

def errors
  @mutex.synchronize { { resources: @resource_errors.dup, error: @error } }
end

#errors?Boolean

Returns whether a global error or any per-resource error exists.

Returns:

  • (Boolean)

    whether a global error or any per-resource error exists



70
71
72
# File 'lib/keycardai/oauth/access_context.rb', line 70

def errors?
  @mutex.synchronize { !@error.nil? || !@resource_errors.empty? }
end

#failed_resourcesArray<String>

Returns resources with a recorded error.

Returns:

  • (Array<String>)

    resources with a recorded error



90
91
92
# File 'lib/keycardai/oauth/access_context.rb', line 90

def failed_resources
  @mutex.synchronize { @resource_errors.keys }
end

#merge(other) ⇒ void

This method returns an undefined value.

Merge another context's tokens and errors into this one.

Parameters:



139
140
141
142
143
144
145
146
# File 'lib/keycardai/oauth/access_context.rb', line 139

def merge(other)
  snapshot = other.errors
  unless snapshot[:error]
    set_bulk_tokens(other.successful_resources.to_h { |resource| [resource, other.access(resource)] })
  end
  snapshot[:resources].each { |resource, error| set_resource_error(resource, error) }
  set_error(snapshot[:error]) if snapshot[:error]
end

#resource_error(resource) ⇒ Object?

Returns the recorded error for the resource, nil when it succeeded.

Parameters:

  • resource (String)

Returns:

  • (Object, nil)

    the recorded error for the resource, nil when it succeeded



44
45
46
# File 'lib/keycardai/oauth/access_context.rb', line 44

def resource_error(resource)
  @mutex.synchronize { @resource_errors[resource] }
end

#resource_error?(resource) ⇒ Boolean

Parameters:

  • resource (String)

Returns:

  • (Boolean)


60
61
62
# File 'lib/keycardai/oauth/access_context.rb', line 60

def resource_error?(resource)
  @mutex.synchronize { @resource_errors.key?(resource) }
end

#set_bulk_tokens(tokens) ⇒ void

This method returns an undefined value.

Record multiple successful tokens at once.

Parameters:



109
110
111
112
113
114
115
116
# File 'lib/keycardai/oauth/access_context.rb', line 109

def set_bulk_tokens(tokens)
  @mutex.synchronize do
    tokens.each do |resource, token|
      @resource_errors.delete(resource)
      @tokens[resource] = token
    end
  end
end

#set_error(error) ⇒ void

This method returns an undefined value.

Record a global (context-wide) error.



131
132
133
# File 'lib/keycardai/oauth/access_context.rb', line 131

def set_error(error)
  @mutex.synchronize { @error = error }
end

#set_resource_error(resource, error) ⇒ void

This method returns an undefined value.

Record a per-resource error, clearing any prior token for the resource.



121
122
123
124
125
126
# File 'lib/keycardai/oauth/access_context.rb', line 121

def set_resource_error(resource, error)
  @mutex.synchronize do
    @tokens.delete(resource)
    @resource_errors[resource] = error
  end
end

#set_token(resource, token) ⇒ void

This method returns an undefined value.

Record a successful token, clearing any prior error for the resource. Called by the grant layer, not by handlers.



98
99
100
101
102
103
# File 'lib/keycardai/oauth/access_context.rb', line 98

def set_token(resource, token)
  @mutex.synchronize do
    @resource_errors.delete(resource)
    @tokens[resource] = token
  end
end

#status"success", ...

Returns:

  • ("success", "partial_error", "error")


75
76
77
78
79
80
81
82
# File 'lib/keycardai/oauth/access_context.rb', line 75

def status
  @mutex.synchronize do
    next "error" unless @error.nil?
    next "partial_error" unless @resource_errors.empty?

    "success"
  end
end

#successful_resourcesArray<String>

Returns resources holding a successful token.

Returns:

  • (Array<String>)

    resources holding a successful token



85
86
87
# File 'lib/keycardai/oauth/access_context.rb', line 85

def successful_resources
  @mutex.synchronize { @tokens.keys }
end