petstore_api_client 0.1.0
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/.editorconfig +33 -0
- data/.env.example +50 -0
- data/.github/CODEOWNERS +36 -0
- data/.github/workflows/ci.yml +157 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTORS.md +39 -0
- data/LICENSE +21 -0
- data/README.md +684 -0
- data/Rakefile +12 -0
- data/lib/petstore_api_client/api_client.rb +60 -0
- data/lib/petstore_api_client/authentication/api_key.rb +107 -0
- data/lib/petstore_api_client/authentication/base.rb +113 -0
- data/lib/petstore_api_client/authentication/composite.rb +178 -0
- data/lib/petstore_api_client/authentication/none.rb +42 -0
- data/lib/petstore_api_client/authentication/oauth2.rb +305 -0
- data/lib/petstore_api_client/client.rb +87 -0
- data/lib/petstore_api_client/clients/concerns/pagination.rb +124 -0
- data/lib/petstore_api_client/clients/concerns/resource_operations.rb +121 -0
- data/lib/petstore_api_client/clients/pet_client.rb +119 -0
- data/lib/petstore_api_client/clients/store_client.rb +37 -0
- data/lib/petstore_api_client/configuration.rb +318 -0
- data/lib/petstore_api_client/connection.rb +55 -0
- data/lib/petstore_api_client/errors.rb +70 -0
- data/lib/petstore_api_client/middleware/authentication.rb +44 -0
- data/lib/petstore_api_client/models/api_response.rb +31 -0
- data/lib/petstore_api_client/models/base.rb +60 -0
- data/lib/petstore_api_client/models/category.rb +17 -0
- data/lib/petstore_api_client/models/named_entity.rb +36 -0
- data/lib/petstore_api_client/models/order.rb +55 -0
- data/lib/petstore_api_client/models/pet.rb +225 -0
- data/lib/petstore_api_client/models/tag.rb +20 -0
- data/lib/petstore_api_client/paginated_collection.rb +133 -0
- data/lib/petstore_api_client/request.rb +225 -0
- data/lib/petstore_api_client/response.rb +193 -0
- data/lib/petstore_api_client/validators/array_presence_validator.rb +15 -0
- data/lib/petstore_api_client/validators/enum_validator.rb +17 -0
- data/lib/petstore_api_client/version.rb +5 -0
- data/lib/petstore_api_client.rb +55 -0
- metadata +252 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PetstoreApiClient
|
|
4
|
+
# Main API client - this is what users interact with
|
|
5
|
+
class ApiClient
|
|
6
|
+
attr_reader :configuration
|
|
7
|
+
|
|
8
|
+
def initialize(config = nil)
|
|
9
|
+
@configuration = config || Configuration.new
|
|
10
|
+
@configuration.validate!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def configure
|
|
14
|
+
yield(configuration) if block_given?
|
|
15
|
+
# Reset clients when configuration changes
|
|
16
|
+
@pet_client = nil
|
|
17
|
+
@store_client = nil
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Access to Pet endpoints
|
|
22
|
+
def pets
|
|
23
|
+
@pets ||= Clients::PetClient.new(configuration)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Access to Store endpoints
|
|
27
|
+
def store
|
|
28
|
+
@store ||= Clients::StoreClient.new(configuration)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Convenience methods so you can do client.create_pet instead of client.pets.create_pet
|
|
32
|
+
def create_pet(pet_data)
|
|
33
|
+
pets.create_pet(pet_data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_pet(pet_id)
|
|
37
|
+
pets.get_pet(pet_id)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def update_pet(pet_data)
|
|
41
|
+
pets.update_pet(pet_data)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def delete_pet(pet_id)
|
|
45
|
+
pets.delete_pet(pet_id)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_order(order_data)
|
|
49
|
+
store.create_order(order_data)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def get_order(order_id)
|
|
53
|
+
store.get_order(order_id)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def delete_order(order_id)
|
|
57
|
+
store.delete_order(order_id)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module PetstoreApiClient
|
|
6
|
+
module Authentication
|
|
7
|
+
# API Key authentication strategy
|
|
8
|
+
# Adds api_key header to requests
|
|
9
|
+
#
|
|
10
|
+
# The Swagger Petstore API accepts api_key in the request header.
|
|
11
|
+
# According to official docs, the special key is "special-key"
|
|
12
|
+
#
|
|
13
|
+
# Security best practices:
|
|
14
|
+
# - API keys are validated before use
|
|
15
|
+
# - Warnings issued for insecure (HTTP) connections
|
|
16
|
+
# - Supports loading from environment variables
|
|
17
|
+
#
|
|
18
|
+
# @example Direct instantiation
|
|
19
|
+
# auth = ApiKey.new("special-key")
|
|
20
|
+
#
|
|
21
|
+
# @example From environment variable
|
|
22
|
+
# ENV['PETSTORE_API_KEY'] = "special-key"
|
|
23
|
+
# auth = ApiKey.from_env
|
|
24
|
+
class ApiKey < Base
|
|
25
|
+
# Header name for API key
|
|
26
|
+
# According to Swagger Petstore spec: https://petstore.swagger.io
|
|
27
|
+
HEADER_NAME = "api_key"
|
|
28
|
+
|
|
29
|
+
# Environment variable name for API key
|
|
30
|
+
ENV_VAR_NAME = "PETSTORE_API_KEY"
|
|
31
|
+
|
|
32
|
+
# Minimum length for API key (security validation)
|
|
33
|
+
MIN_KEY_LENGTH = 3
|
|
34
|
+
|
|
35
|
+
attr_reader :api_key
|
|
36
|
+
|
|
37
|
+
# Initialize API key authenticator
|
|
38
|
+
#
|
|
39
|
+
# @param api_key [String, nil] The API key to use
|
|
40
|
+
# @raise [ValidationError] if API key is invalid
|
|
41
|
+
# rubocop:disable Lint/MissingSuper
|
|
42
|
+
def initialize(api_key = nil)
|
|
43
|
+
@api_key = api_key&.to_s&.strip
|
|
44
|
+
validate! if configured?
|
|
45
|
+
end
|
|
46
|
+
# rubocop:enable Lint/MissingSuper
|
|
47
|
+
|
|
48
|
+
# Create authenticator from environment variable
|
|
49
|
+
#
|
|
50
|
+
# @return [ApiKey] New authenticator instance
|
|
51
|
+
def self.from_env
|
|
52
|
+
new(ENV.fetch(ENV_VAR_NAME, nil))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Apply API key authentication to request
|
|
56
|
+
# Adds api_key header to the request
|
|
57
|
+
#
|
|
58
|
+
# @param env [Faraday::Env] The request environment
|
|
59
|
+
# @return [void]
|
|
60
|
+
def apply(env)
|
|
61
|
+
return unless configured?
|
|
62
|
+
|
|
63
|
+
# Warn if sending API key over insecure connection (HTTP)
|
|
64
|
+
warn_if_insecure!(env)
|
|
65
|
+
|
|
66
|
+
# Add API key header
|
|
67
|
+
env.request_headers[HEADER_NAME] = @api_key
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Check if API key is configured
|
|
71
|
+
#
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
def configured?
|
|
74
|
+
!@api_key.nil? && !@api_key.empty?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# String representation (masks API key for security)
|
|
78
|
+
#
|
|
79
|
+
# @return [String]
|
|
80
|
+
def inspect
|
|
81
|
+
return unconfigured_inspect unless configured?
|
|
82
|
+
|
|
83
|
+
# Use base class method to mask credential (show first 4 chars)
|
|
84
|
+
masked = mask_credential(@api_key, 4)
|
|
85
|
+
|
|
86
|
+
"#<#{self.class.name} api_key=#{masked}>"
|
|
87
|
+
end
|
|
88
|
+
alias to_s inspect
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Validate API key format and length
|
|
93
|
+
#
|
|
94
|
+
# @raise [ValidationError] if API key is invalid
|
|
95
|
+
def validate!
|
|
96
|
+
return unless configured?
|
|
97
|
+
|
|
98
|
+
# Use base class validation methods for DRY principle
|
|
99
|
+
validate_credential_length(@api_key, "API key", MIN_KEY_LENGTH)
|
|
100
|
+
validate_no_newlines(@api_key, "API key")
|
|
101
|
+
validate_no_whitespace(@api_key, "API key")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Note: warn_if_insecure! method inherited from Base class
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PetstoreApiClient
|
|
4
|
+
module Authentication
|
|
5
|
+
# Base class for authentication strategies
|
|
6
|
+
# Implements Strategy Pattern - allows different authentication methods
|
|
7
|
+
# to be swapped without changing client code
|
|
8
|
+
#
|
|
9
|
+
# This follows the same pattern as battle-tested gems like:
|
|
10
|
+
# - Octokit (GitHub API client)
|
|
11
|
+
# - Slack-ruby-client
|
|
12
|
+
# - Stripe Ruby library
|
|
13
|
+
class Base
|
|
14
|
+
# Apply authentication to a Faraday request environment
|
|
15
|
+
#
|
|
16
|
+
# @param env [Faraday::Env] The request environment
|
|
17
|
+
# @return [void]
|
|
18
|
+
def apply(env)
|
|
19
|
+
raise NotImplementedError, "#{self.class.name} must implement #apply"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Check if this authentication strategy is configured
|
|
23
|
+
# Used to determine if auth should be applied
|
|
24
|
+
#
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
def configured?
|
|
27
|
+
raise NotImplementedError, "#{self.class.name} must implement #configured?"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Human-readable description of authentication type
|
|
31
|
+
# Useful for logging and debugging
|
|
32
|
+
#
|
|
33
|
+
# @return [String]
|
|
34
|
+
def type
|
|
35
|
+
self.class.name.split("::").last
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
# Validates that a credential meets minimum length requirements
|
|
41
|
+
#
|
|
42
|
+
# @param value [String] The credential value to validate
|
|
43
|
+
# @param field_name [String] Name of the field for error messages
|
|
44
|
+
# @param min_length [Integer] Minimum required length
|
|
45
|
+
# @raise [ValidationError] if credential is too short
|
|
46
|
+
# @return [void]
|
|
47
|
+
def validate_credential_length(value, field_name, min_length)
|
|
48
|
+
return if value.length >= min_length
|
|
49
|
+
|
|
50
|
+
raise ValidationError,
|
|
51
|
+
"#{field_name} must be at least #{min_length} characters (got #{value.length})"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Validates that a credential doesn't contain newline characters
|
|
55
|
+
# Newlines can cause security issues and are never valid in credentials
|
|
56
|
+
#
|
|
57
|
+
# @param value [String] The credential value to validate
|
|
58
|
+
# @param field_name [String] Name of the field for error messages
|
|
59
|
+
# @raise [ValidationError] if credential contains newlines
|
|
60
|
+
# @return [void]
|
|
61
|
+
def validate_no_newlines(value, field_name)
|
|
62
|
+
return unless value.include?("\n") || value.include?("\r")
|
|
63
|
+
|
|
64
|
+
raise ValidationError, "#{field_name} contains newline characters"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Validates that a credential doesn't have leading/trailing whitespace
|
|
68
|
+
# Whitespace usually indicates copy-paste errors
|
|
69
|
+
#
|
|
70
|
+
# @param value [String] The credential value to validate
|
|
71
|
+
# @param field_name [String] Name of the field for error messages
|
|
72
|
+
# @raise [ValidationError] if credential has whitespace
|
|
73
|
+
# @return [void]
|
|
74
|
+
def validate_no_whitespace(value, field_name)
|
|
75
|
+
return if value == value.strip
|
|
76
|
+
|
|
77
|
+
raise ValidationError,
|
|
78
|
+
"#{field_name} has leading/trailing whitespace (did you copy-paste incorrectly?)"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Warns if authentication is being sent over insecure HTTP connection
|
|
82
|
+
# This is a security risk as credentials can be intercepted
|
|
83
|
+
#
|
|
84
|
+
# @param env [Faraday::Env] The request environment
|
|
85
|
+
# @return [void]
|
|
86
|
+
def warn_if_insecure!(env)
|
|
87
|
+
return if env.url.scheme == "https"
|
|
88
|
+
|
|
89
|
+
warn "[PetstoreApiClient] WARNING: Sending credentials over insecure HTTP connection! " \
|
|
90
|
+
"Use HTTPS in production to protect credentials."
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Masks a credential for safe display in logs and inspect output
|
|
94
|
+
# Shows first few characters and masks the rest
|
|
95
|
+
#
|
|
96
|
+
# @param value [String] The credential to mask
|
|
97
|
+
# @param visible_chars [Integer] Number of characters to show (default: 3)
|
|
98
|
+
# @return [String] Masked credential string
|
|
99
|
+
def mask_credential(value, visible_chars = 3)
|
|
100
|
+
return "***" if value.length <= visible_chars
|
|
101
|
+
|
|
102
|
+
"#{value[0...visible_chars]}#{"*" * (value.length - visible_chars)}"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Standard inspect message for unconfigured authenticators
|
|
106
|
+
#
|
|
107
|
+
# @return [String] Inspect message
|
|
108
|
+
def unconfigured_inspect
|
|
109
|
+
"#<#{self.class.name} (not configured)>"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module PetstoreApiClient
|
|
6
|
+
module Authentication
|
|
7
|
+
# Composite authentication strategy for applying multiple auth methods simultaneously
|
|
8
|
+
#
|
|
9
|
+
# This authenticator implements the Composite pattern, allowing multiple authentication
|
|
10
|
+
# strategies to be applied to a single request. This is particularly useful during
|
|
11
|
+
# migration periods when transitioning from one auth method to another, or when an
|
|
12
|
+
# API accepts multiple authentication methods.
|
|
13
|
+
#
|
|
14
|
+
# The Petstore API accepts both API Key and OAuth2 authentication. Using this composite
|
|
15
|
+
# authenticator, you can send both headers simultaneously, allowing the server to
|
|
16
|
+
# accept either authentication method.
|
|
17
|
+
#
|
|
18
|
+
# All configured strategies are applied in the order they were added. Each strategy's
|
|
19
|
+
# apply() method is called, allowing it to add its own headers to the request.
|
|
20
|
+
#
|
|
21
|
+
# @example Using both API Key and OAuth2
|
|
22
|
+
# api_key_auth = ApiKey.new("special-key")
|
|
23
|
+
# oauth2_auth = OAuth2.new(client_id: "id", client_secret: "secret")
|
|
24
|
+
# composite = Composite.new([api_key_auth, oauth2_auth])
|
|
25
|
+
#
|
|
26
|
+
# composite.apply(env)
|
|
27
|
+
# # Request now has both:
|
|
28
|
+
# # - api_key: special-key
|
|
29
|
+
# # - Authorization: Bearer <token>
|
|
30
|
+
#
|
|
31
|
+
# @example Building from configuration
|
|
32
|
+
# strategies = []
|
|
33
|
+
# strategies << ApiKey.new(config.api_key) if config.api_key
|
|
34
|
+
# strategies << OAuth2.new(...) if config.oauth2_client_id
|
|
35
|
+
# composite = Composite.new(strategies)
|
|
36
|
+
#
|
|
37
|
+
# @see https://refactoring.guru/design-patterns/composite Composite Pattern
|
|
38
|
+
# @since 0.2.0
|
|
39
|
+
class Composite < Base
|
|
40
|
+
# @!attribute [r] strategies
|
|
41
|
+
# @return [Array<Base>] List of authentication strategies to apply
|
|
42
|
+
attr_reader :strategies
|
|
43
|
+
|
|
44
|
+
# Initialize composite authenticator with multiple strategies
|
|
45
|
+
#
|
|
46
|
+
# @param strategies [Array<Base>] List of authentication strategies
|
|
47
|
+
# Each strategy must respond to #apply(env) and #configured?
|
|
48
|
+
#
|
|
49
|
+
# @raise [ArgumentError] if strategies is not an array
|
|
50
|
+
# @raise [ArgumentError] if any strategy doesn't inherit from Base
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# api_key = ApiKey.new("my-key")
|
|
54
|
+
# oauth2 = OAuth2.new(client_id: "id", client_secret: "secret")
|
|
55
|
+
# composite = Composite.new([api_key, oauth2])
|
|
56
|
+
#
|
|
57
|
+
# rubocop:disable Lint/MissingSuper
|
|
58
|
+
def initialize(strategies = [])
|
|
59
|
+
raise ArgumentError, "strategies must be an Array (got #{strategies.class})" unless strategies.is_a?(Array)
|
|
60
|
+
|
|
61
|
+
validate_strategies!(strategies)
|
|
62
|
+
@strategies = strategies
|
|
63
|
+
# Performance optimization: cache configured strategies to avoid repeated checks
|
|
64
|
+
@configured_strategies = @strategies.select(&:configured?)
|
|
65
|
+
end
|
|
66
|
+
# rubocop:enable Lint/MissingSuper
|
|
67
|
+
|
|
68
|
+
# Apply all configured authentication strategies to the request
|
|
69
|
+
#
|
|
70
|
+
# Iterates through all strategies and calls apply() on each one if it's configured.
|
|
71
|
+
# This allows multiple authentication headers to be added to the same request.
|
|
72
|
+
#
|
|
73
|
+
# @param env [Faraday::Env] The Faraday request environment
|
|
74
|
+
# @return [void]
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# composite = Composite.new([api_key_auth, oauth2_auth])
|
|
78
|
+
# composite.apply(env)
|
|
79
|
+
# # Both authentication methods are now applied
|
|
80
|
+
#
|
|
81
|
+
def apply(env)
|
|
82
|
+
# Performance optimization: use cached configured strategies
|
|
83
|
+
# Avoids checking configured? on every request
|
|
84
|
+
@configured_strategies.each { |strategy| strategy.apply(env) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check if any authentication strategy is configured
|
|
88
|
+
#
|
|
89
|
+
# Returns true if at least one strategy in the composite is configured.
|
|
90
|
+
# Returns false if no strategies are configured or if strategies array is empty.
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] true if at least one strategy is configured
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# # No strategies configured
|
|
96
|
+
# composite = Composite.new([])
|
|
97
|
+
# composite.configured? # => false
|
|
98
|
+
#
|
|
99
|
+
# # One strategy configured
|
|
100
|
+
# api_key = ApiKey.new("my-key")
|
|
101
|
+
# composite = Composite.new([api_key])
|
|
102
|
+
# composite.configured? # => true
|
|
103
|
+
#
|
|
104
|
+
# # Mixed (one configured, one not)
|
|
105
|
+
# oauth2 = OAuth2.new # Not configured (no credentials)
|
|
106
|
+
# composite = Composite.new([api_key, oauth2])
|
|
107
|
+
# composite.configured? # => true (at least one is configured)
|
|
108
|
+
#
|
|
109
|
+
def configured?
|
|
110
|
+
# Performance optimization: use cached configured strategies
|
|
111
|
+
!@configured_strategies.empty?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get list of configured strategy types
|
|
115
|
+
#
|
|
116
|
+
# Returns array of type names for strategies that are actually configured.
|
|
117
|
+
# Useful for debugging and logging.
|
|
118
|
+
#
|
|
119
|
+
# @return [Array<String>] List of configured strategy type names
|
|
120
|
+
#
|
|
121
|
+
# @example
|
|
122
|
+
# api_key = ApiKey.new("key")
|
|
123
|
+
# oauth2 = OAuth2.new # Not configured
|
|
124
|
+
# composite = Composite.new([api_key, oauth2])
|
|
125
|
+
# composite.configured_types # => ["ApiKey"]
|
|
126
|
+
#
|
|
127
|
+
def configured_types
|
|
128
|
+
@strategies.select(&:configured?).map(&:type)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# String representation showing all configured strategies
|
|
132
|
+
#
|
|
133
|
+
# @return [String] Human-readable representation
|
|
134
|
+
#
|
|
135
|
+
# @example
|
|
136
|
+
# composite = Composite.new([api_key, oauth2])
|
|
137
|
+
# composite.inspect
|
|
138
|
+
# # => "#<Composite strategies=[ApiKey, OAuth2] configured=[ApiKey, OAuth2]>"
|
|
139
|
+
#
|
|
140
|
+
def inspect
|
|
141
|
+
all_types = @strategies.map(&:type).join(", ")
|
|
142
|
+
configured = configured_types.join(", ")
|
|
143
|
+
configured = "none" if configured.empty?
|
|
144
|
+
|
|
145
|
+
"#<#{self.class.name} strategies=[#{all_types}] configured=[#{configured}]>"
|
|
146
|
+
end
|
|
147
|
+
alias to_s inspect
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
# Validate that all strategies are valid authentication strategy objects
|
|
152
|
+
#
|
|
153
|
+
# @param strategies [Array] List of strategies to validate
|
|
154
|
+
# @return [void]
|
|
155
|
+
# @raise [ArgumentError] if any strategy is invalid
|
|
156
|
+
#
|
|
157
|
+
def validate_strategies!(strategies)
|
|
158
|
+
strategies.each_with_index do |strategy, index|
|
|
159
|
+
unless strategy.is_a?(Base)
|
|
160
|
+
raise ArgumentError,
|
|
161
|
+
"Strategy at index #{index} must inherit from Authentication::Base " \
|
|
162
|
+
"(got #{strategy.class})"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
unless strategy.respond_to?(:apply)
|
|
166
|
+
raise ArgumentError,
|
|
167
|
+
"Strategy at index #{index} (#{strategy.class}) must respond to #apply"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
unless strategy.respond_to?(:configured?)
|
|
171
|
+
raise ArgumentError,
|
|
172
|
+
"Strategy at index #{index} (#{strategy.class}) must respond to #configured?"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module PetstoreApiClient
|
|
6
|
+
module Authentication
|
|
7
|
+
# No authentication strategy (Null Object pattern)
|
|
8
|
+
# Used when no authentication is configured
|
|
9
|
+
#
|
|
10
|
+
# This allows the authentication system to always have an authenticator
|
|
11
|
+
# without needing nil checks everywhere
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# auth = None.new
|
|
15
|
+
# auth.configured? # => false
|
|
16
|
+
# auth.apply(env) # Does nothing
|
|
17
|
+
class None < Base
|
|
18
|
+
# Apply no authentication (does nothing)
|
|
19
|
+
#
|
|
20
|
+
# @param _env [Faraday::Env] The request environment (unused)
|
|
21
|
+
# @return [void]
|
|
22
|
+
def apply(_env)
|
|
23
|
+
# Intentionally empty - no authentication to apply
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Check if authentication is configured
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean] Always false
|
|
29
|
+
def configured?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# String representation
|
|
34
|
+
#
|
|
35
|
+
# @return [String]
|
|
36
|
+
def inspect
|
|
37
|
+
"#<#{self.class.name} (no authentication)>"
|
|
38
|
+
end
|
|
39
|
+
alias to_s inspect
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|