Skip to content

Commit be0f117

Browse files
committed
Introduce CORE Api Authentication
A new version of the API (CORE) is now available and will be used in the near future for new resources. This new API utilizes a basic authentication scheme with an encoded username/password in the header. This PR lays the groundwork for future PR's to be able to interact with the CORE api, and adds the plumbing necessary to successfully configure the gem, and authenticate with CORE. This change addresses the need by: * Introducing a v2 authenticator * Introducing a v2 api * Introducing a CoreObject resource which can be used by all core (v2) objects
1 parent 9484040 commit be0f117

9 files changed

Lines changed: 272 additions & 5 deletions

File tree

‎lib/bls/core.rb‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
require_relative "core/api_client"
99
require_relative "core/api/v1"
10+
require_relative "core/api/v2"
1011
require_relative "core/api/v1/authenticator"
12+
require_relative "core/api/v2/authenticator"
1113
require_relative "core/api_operations/retrieve"
1214
require_relative "core/authorization/base"
1315
require_relative "core/authorization/basic"
@@ -32,7 +34,8 @@ module Core
3234
class Error < StandardError; end
3335

3436
class << self
35-
attr_accessor :client_id, :client_secret, :environment
37+
attr_accessor :client_id, :client_secret, :environment, :username,
38+
:password
3639

3740
def configure
3841
yield self

‎lib/bls/core/api/v1/authenticator.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def save!
1515
path = "#{base_url}/token"
1616
body = { client_id: client_id, client_secret: client_secret }
1717
auth = Authorization::Basic.
18-
factory(client_id: client_id, client_secret: client_secret)
18+
factory(username: client_id, password: client_secret)
1919
client = Core::ApiClient.new(authentication: auth)
2020
response = client.post(path: path, body: body)
2121
handle_authentication_response!(response: response.data)

‎lib/bls/core/api/v2.rb‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
module Bls
4+
module Core
5+
class V2
6+
attr_accessor :authentication
7+
attr_reader :environment, :username, :password
8+
9+
PRODUCTION = "production"
10+
STAGING = "staging"
11+
12+
def initialize(username: nil, password: nil, authentication: nil,
13+
environment: STAGING)
14+
@authentication = authentication
15+
@environment = environment
16+
@username = username
17+
@password = password
18+
end
19+
20+
def base_url
21+
"https://core.batchlinesolutions.#{tld}"
22+
end
23+
24+
def authenticate!
25+
return if authenticated?
26+
27+
authenticator = V2::Authenticator.build(self)
28+
@authentication = authenticator.save!
29+
end
30+
31+
def client
32+
build_api_client!
33+
end
34+
35+
def authenticated?
36+
authentication.present?
37+
end
38+
39+
def reset_authentication
40+
@authentication = nil
41+
self
42+
end
43+
44+
def self.build(config: Bls::Core)
45+
new(environment: config.environment,
46+
username: config.username,
47+
password: config.password)
48+
end
49+
50+
protected
51+
52+
def build_api_client!
53+
authenticate!
54+
Bls::Core::ApiClient.build(self)
55+
end
56+
57+
def tld
58+
@tld ||= determine_tld
59+
end
60+
61+
def determine_tld
62+
if environment == PRODUCTION
63+
"com"
64+
else
65+
"dev"
66+
end
67+
end
68+
end
69+
end
70+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Bls
4+
module Core
5+
class V2::Authenticator
6+
attr_accessor :authentication
7+
8+
def initialize(authentication:)
9+
@authentication = authentication
10+
end
11+
12+
def save!
13+
authentication
14+
end
15+
16+
def self.build(api)
17+
authentication = Authorization::Basic.
18+
factory(username: api.username,
19+
password: api.password)
20+
new(authentication: authentication)
21+
end
22+
end
23+
end
24+
end

‎lib/bls/core/authorization/basic.rb‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ def header
88
"Basic #{token}"
99
end
1010

11-
def self.factory(client_id:, client_secret:)
12-
encoded_token = Base64.strict_encode64("#{client_id}:#{client_secret}")
11+
def self.factory(username:, password:)
12+
encoded_token = Base64.strict_encode64("#{username}:#{password}")
1313
new(token: encoded_token)
1414
end
1515
end

‎lib/bls/core/resources/api_error.rb‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ def message
2222

2323
def self.build(response)
2424
data = response.data
25-
data => { status:, title:, details: }
25+
status = data.fetch(:status, data[:type])
26+
title = data.fetch(:title, data[:message])
27+
details = data[:details]
2628
new(status: status, title: title, details: details)
2729
end
2830

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe Bls::Core::V2::Authenticator do
4+
describe "#save!" do
5+
it "returns a authorization scheme" do
6+
basic = Bls::Core::Authorization::Basic.new(token: "test")
7+
auth = Bls::Core::V2::Authenticator.new(authentication: basic)
8+
9+
result = auth.save!
10+
11+
expect(result).to respond_to(:header)
12+
end
13+
end
14+
15+
describe ".build" do
16+
it "sets the authentication" do
17+
api = Bls::Core::V2.new
18+
basic = Bls::Core::Authorization::Basic.new(token: "test")
19+
allow(Bls::Core::Authorization::Basic).to receive(:factory).
20+
and_return(basic)
21+
22+
result = Bls::Core::V2::Authenticator.build(api)
23+
24+
expect(result.authentication).to eq(basic)
25+
end
26+
end
27+
28+
def build_config(environment: "staging", client_id: "cid_123",
29+
client_secret: "cs_123")
30+
OpenStruct.new(environment: environment, client_id: client_id,
31+
client_secret: client_secret)
32+
end
33+
end

‎spec/core/api/v2_spec.rb‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe Bls::Core::V2 do
4+
describe "#base_url" do
5+
context "when the environment is production" do
6+
it "returns the correct url" do
7+
api = Bls::Core::V2.new(environment: "production")
8+
9+
result = api.base_url
10+
11+
expect(result).to eq("https://core.batchlinesolutions.com")
12+
end
13+
end
14+
15+
context "when the environment is not production" do
16+
it "returns the correct url" do
17+
api = Bls::Core::V2.new(environment: "staging")
18+
19+
result = api.base_url
20+
21+
expect(result).to eq("https://core.batchlinesolutions.dev")
22+
end
23+
end
24+
end
25+
26+
describe "#client" do
27+
it "returns a api client" do
28+
api = Bls::Core::V2.new
29+
build_stubbed_authenticator(api)
30+
31+
result = api.client
32+
33+
expect(result).to respond_to(:post)
34+
end
35+
end
36+
37+
describe "#authenticate" do
38+
it "builds a authenticator" do
39+
api = Bls::Core::V2.new
40+
build_stubbed_authenticator(api)
41+
42+
api.authenticate!
43+
44+
expect(Bls::Core::V2::Authenticator).to have_received(:build).once
45+
end
46+
47+
it "authenticates" do
48+
api = Bls::Core::V2.new
49+
authenticator = build_stubbed_authenticator(api)
50+
51+
api.authenticate!
52+
53+
expect(authenticator).to have_received(:save!).once
54+
end
55+
56+
context "when the authentication is present" do
57+
it "is authenticated" do
58+
api = Bls::Core::V2.new
59+
build_stubbed_authenticator(api)
60+
61+
api.authenticate!
62+
63+
expect(api).to be_authenticated
64+
end
65+
end
66+
end
67+
68+
describe "reset authentication" do
69+
it "removes the present authentication" do
70+
auth = Bls::Core::Authorization::Basic.new(token: "1234")
71+
api = Bls::Core::V2.new(authentication: auth)
72+
73+
result = api.reset_authentication
74+
75+
expect(result.authentication).to be_blank
76+
end
77+
78+
it "is not authenticated" do
79+
auth = Bls::Core::Authorization::Basic.new(token: "1234")
80+
api = Bls::Core::V2.new(authentication: auth)
81+
82+
result = api.reset_authentication
83+
84+
expect(result).not_to be_authenticated
85+
end
86+
end
87+
88+
describe ".build" do
89+
it "sets the environment" do
90+
config = build_config(environment: "preview")
91+
92+
result = Bls::Core::V2.build(config: config)
93+
94+
expect(result.environment).to eq("preview")
95+
end
96+
97+
it "sets the username" do
98+
config = build_config(username: "secretUsername")
99+
100+
result = Bls::Core::V2.build(config: config)
101+
102+
expect(result.username).to eq("secretUsername")
103+
end
104+
105+
it "sets the password" do
106+
config = build_config(password: "secretPassword")
107+
108+
result = Bls::Core::V2.build(config: config)
109+
110+
expect(result.password).to eq("secretPassword")
111+
end
112+
end
113+
114+
def build_config(environment: "staging", username: "string",
115+
password: "string")
116+
OpenStruct.new(environment: environment, username: username,
117+
password: password)
118+
end
119+
120+
def build_stubbed_authenticator(api)
121+
authenticator = Bls::Core::V2::Authenticator.build(api)
122+
authentication = authenticator.authentication
123+
client = Bls::Core::ApiClient.new(authentication: authentication)
124+
allow(Bls::Core::V2::Authenticator).to receive(:build).
125+
and_return(authenticator)
126+
allow(authenticator).to receive(:save!).and_return(client)
127+
authenticator
128+
end
129+
end

‎spec/support/helpers/api_helper.rb‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ def stub_api_v1_authentication(authenticated: true)
2828
allow(api).to receive(:authenticated?).and_return(authenticated)
2929
end
3030

31+
def stub_api_v2_authentication(authenticated: true)
32+
api = Bls::Core::V2.build
33+
allow(Bls::Core::V2).to receive(:new).and_return(api)
34+
allow(api).to receive(:authenticated?).and_return(authenticated)
35+
end
36+
3137
def stub_api_response(response: OpenStruct.new(body: "{}", code: 200),
3238
http: double)
3339
allow(Net::HTTP).to receive(:start).and_yield(http)

0 commit comments

Comments
 (0)