-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Spike on a full redesign of the client
Fixes #261 TODO: write a better commit message later when this is more fully baked.
- Loading branch information
Showing
25 changed files
with
1,124 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'logger' | ||
require 'singleton' | ||
|
||
module SdrClient | ||
# The SDR client reimagined, built using patterns successfully used in other client gems we maintain | ||
class RedesignedClient | ||
include Singleton | ||
|
||
DEFAULT_HEADERS = { | ||
accept: 'application/json', | ||
content_type: 'application/json' | ||
}.freeze | ||
|
||
class << self | ||
def configure(url:, email:, password:, request_options: default_request_options, logger: default_logger) | ||
instance.config = Config.new( | ||
url: url, | ||
email: email, | ||
password: password, | ||
request_options: request_options, | ||
logger: logger | ||
) | ||
|
||
instance | ||
end | ||
|
||
def default_logger | ||
Logger.new($stdout) | ||
end | ||
|
||
def default_request_options | ||
{ | ||
read_timeout: default_timeout, | ||
timeout: default_timeout | ||
} | ||
end | ||
|
||
# NOTE: This is the number of seconds it roughly takes for H2 to | ||
# successfully shunt ~10GB files over to SDR API | ||
def default_timeout | ||
900 | ||
end | ||
|
||
delegate :config, :connection, :deposit_model, :job_status, to: :instance | ||
end | ||
|
||
attr_accessor :config | ||
|
||
def deposit_model(...) | ||
Deposit.deposit_model(...) | ||
end | ||
|
||
def job_status(...) | ||
JobStatus.new(...) | ||
end | ||
|
||
# def token_for(user_id:) | ||
# end | ||
|
||
# def checksum_for(file_path:, algorithm: :md5|:sha1) | ||
# end | ||
|
||
# def find(object_id:) | ||
# end | ||
|
||
# def update_model(model:, version_description:) | ||
# end | ||
|
||
# # NOTE: this method is used only the CLI & the infrastructure-integration-tests deposit helpers | ||
# def deposit_metadata(apo:, collection:, type:, strategies:, files:, path:) | ||
# end | ||
|
||
# Send an authenticated GET request | ||
# @param path [String] the path to the SDR API request | ||
def get(path:) | ||
response = with_token_refresh_when_unauthorized do | ||
connection.get(path) | ||
end | ||
|
||
UnexpectedResponse.call(response) unless response.success? | ||
|
||
return nil if response.body.blank? | ||
|
||
JSON.parse(response.body).with_indifferent_access | ||
end | ||
|
||
# Send an authenticated POST request | ||
# @param path [String] the path to the SDR API request | ||
# @param body [String] the body of the SDR API request | ||
# @param headers [Hash] extra headers to add to the SDR API request | ||
def post(path:, body:, headers: {}) | ||
response = with_token_refresh_when_unauthorized do | ||
connection.post(path) do |conn| | ||
conn.body = body.to_json | ||
conn.headers = headers | ||
end | ||
end | ||
|
||
UnexpectedResponse.call(response) unless response.success? | ||
|
||
return nil if response.body.blank? | ||
|
||
JSON.parse(response.body).with_indifferent_access | ||
end | ||
|
||
# Send an authenticated PUT request | ||
# @param path [String] the path to the SDR API request | ||
# @param body [String] the body of the SDR API request | ||
def put(path:, body:, content_type:, content_length:) | ||
request_body = content_type == 'application/json' ? body.to_json : body | ||
request_headers = { | ||
'content-type': content_type, | ||
'content-length': content_length | ||
} | ||
response = with_token_refresh_when_unauthorized do | ||
connection.put(path) do |conn| | ||
conn.body = request_body | ||
conn.headers = request_headers | ||
end | ||
end | ||
|
||
UnexpectedResponse.call(response) unless response.success? | ||
|
||
return nil if response.body.blank? | ||
|
||
JSON.parse(response.body).with_indifferent_access | ||
end | ||
|
||
private | ||
|
||
Config = Struct.new(:url, :email, :password, :logger, :request_options, keyword_init: true) | ||
|
||
def connection | ||
@connection ||= Faraday.new( | ||
url: config.url, | ||
headers: DEFAULT_HEADERS, | ||
request: config.request_options | ||
) do |conn| | ||
conn.request :authorization, :Bearer, SdrClient::Credentials.read | ||
conn.adapter :net_http | ||
end | ||
end | ||
|
||
def with_token_refresh_when_unauthorized | ||
response = yield | ||
|
||
# if unauthorized, token has likely expired. try to get a new token and then retry the same request(s). | ||
if response.status == 401 | ||
config.token = Authenticator.token | ||
response = yield | ||
end | ||
|
||
response | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
module SdrClient | ||
class RedesignedClient | ||
# Fetch a token from the Folio API using login_params | ||
class Authenticator | ||
include Dry::Monads[:result] | ||
|
||
def self.token | ||
new.token | ||
end | ||
|
||
# Request an access_token | ||
def token | ||
response = SdrClient::RedesignedClient.connection.post(path, request_body) | ||
|
||
UnexpectedResponse.call(response) unless response.success? | ||
|
||
credential_store.write(response.body) | ||
Success() | ||
end | ||
|
||
private | ||
|
||
def request_body | ||
JSON.generate( | ||
{ | ||
email: SdrClient::RedesignedClient.config.email, | ||
password: SdrClient::RedesignedClient.config.password | ||
} | ||
) | ||
end | ||
|
||
def path | ||
'/v1/auth/login' | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# frozen_string_literal: true | ||
|
||
module SdrClient | ||
class RedesignedClient | ||
# Creates a resource (metadata) in SDR | ||
class CreateResource | ||
def self.run(...) | ||
new(...).run | ||
end | ||
|
||
# @param [Boolean] accession should the accessionWF be started | ||
# @param [Boolean] assign_doi should a DOI be assigned to this item | ||
# @param [Cocina::Models::RequestDRO, Cocina::Models::RequestCollection] metadata | ||
# @param [Hash<Symbol,String>] the result of the metadata call | ||
# @param [String] priority what processing priority should be used | ||
# either 'low' or 'default' | ||
def initialize(accession:, metadata:, assign_doi: false, priority: nil) | ||
@accession = accession | ||
@priority = priority | ||
@assign_doi = assign_doi | ||
@metadata = metadata | ||
end | ||
|
||
# @param [Hash<Symbol,String>] the result of the metadata call | ||
# @return [String] job id for the background job result | ||
def run | ||
response = metadata_request | ||
UnexpectedResponse.call(response) unless response.status == 201 | ||
|
||
logger.info("Response from server: #{response.body}") | ||
|
||
JSON.parse(response.body)['jobId'] | ||
end | ||
|
||
private | ||
|
||
attr_reader :metadata, :priority | ||
|
||
def logger | ||
SdrClient::RedesignedClient.config.logger | ||
end | ||
|
||
def client | ||
SdrClient::RedesignedClient.instance | ||
end | ||
|
||
def metadata_request | ||
json = metadata.to_json | ||
logger.debug("Starting upload metadata: #{json}") | ||
|
||
connection.post(path: path, body: json, headers: { 'X-Cocina-Models-Version' => Cocina::Models::VERSION }) | ||
end | ||
|
||
def accession? | ||
@accession | ||
end | ||
|
||
def assign_doi? | ||
@assign_doi | ||
end | ||
|
||
def path | ||
params = { accession: accession? } | ||
params[:priority] = priority if priority | ||
params[:assign_doi] = true if assign_doi? # false is default | ||
query_string = params.map { |k, v| "#{k}=#{v}" }.join('&') | ||
"/v1/resources?#{query_string}" | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.