Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 46 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# Archivesspace Client
# ArchivesSpace Client

Interact with ArchivesSpace via the API.

<!-- TOC start (generated with https://github.com/derlin/bitdowntoc) -->

* [Installation](#installation)
* [Usage](#usage)
+ [Configuring a client](#configuring-a-client)
- [Default configuration](#default-configuration)
- [Custom configuration, on the fly](#custom-configuration-on-the-fly)
- [Custom configuration, stored for use with CLI or console](#custom-configuration-stored-for-use-with-cli-or-console)
+ [Making basic requests](#making-basic-requests)
+ [Setting a repository context](#setting-a-repository-context)
* [Configuring a client](#configuring-a-client)
* [Default configuration](#default-configuration)
* [Custom configuration, on the fly](#custom-configuration-on-the-fly)
* [Custom configuration, stored for use with CLI or console](#custom-configuration-stored-for-use-with-cli-or-console)
* [Making basic requests](#making-basic-requests)
* [Setting a repository context](#setting-a-repository-context)
* [Templates](#templates)
* [CLI](#cli)
* [Console usage](#console-usage)
* [Development](#development)
* [Publishing](#publishing)
* [Changelog](#changelog)
* [Contributing](#contributing)
* [License](#license)

Expand Down Expand Up @@ -56,7 +57,7 @@ Create client with default settings (`localhost:8089`, `admin`, `admin`):
client = ArchivesSpace::Client.new.login
```

#### Custom configuration, on the fly
#### Custom configuration

```ruby
config = ArchivesSpace::Configuration.new({
Expand All @@ -72,8 +73,6 @@ config = ArchivesSpace::Configuration.new({
client = ArchivesSpace::Client.new(config).login
```

**NOTE:** `ArchivesSpace::Configuration` allows you to set a `base_repo` value as well, but if this value is set in your config at the start, calls to API endpoints that do not include a repository id in the URI may not work correctly. It is recommended you set/unset the client repository as needed during use via the `#repository` method as described below. If you must set `base_repo` in the config used to create your client, note that the value should be like: "repositories/2", and not just the integer repository id.

#### Custom configuration, stored for use with CLI or console

Create a file containing JSON config data like:
Expand Down Expand Up @@ -138,20 +137,33 @@ client.get('repositories/2/digital_objects', query: {page: 1})
You can do:

```ruby
client.repository(2)
client.get('digital_objects', query: {page: 1})
client.repository(2) do
client.get('digital_objects', query: {page: 1})
end

# now back in the global scope
```

To reset:
Scopes restore the previous context on exit (even if the block raises) and can
be nested:

```ruby
client.repository(nil)
client.repository(2) do
client.repository(3) do
client.resources # scoped to repo 3
end
client.resources # back to repo 2
end

# now back in the global scope
```

Or:
You can also set the scope persistently (without a block) and clear it later:

```ruby
client.use_global_repository
client.repository(2)
client.resources
client.use_global_repository # or: client.repository(nil)
```

## Templates
Expand Down Expand Up @@ -246,9 +258,26 @@ bundle exec rake
When an updated version (`lib/archivesspace/client/version.rb`) is merged into the
main/master branch a new release will be built and published.

## Changelog

### 0.5.0

Breaking changes:

* Removed the `base_repo` configuration option. Use `client.repository(id)` to
scope requests to a repository instead, either persistently or with a
block that auto-restores the previous scope.
* `client.repository(id)` with a block now saves and restores the previous
context (including when nested or when the block raises), instead of always
resetting to the global scope.
* Login failure now raises `ArchivesSpace::AuthenticationError` (was
`ConnectionError`). `ConnectionError` has been removed.
* `Client.new` raises `ArchivesSpace::ConfigurationError` (was `RuntimeError`)
when given a non-`Configuration` argument.

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/lyrasis/archivesspace-client.
Bug reports and pull requests are welcome on GitHub at <https://github.com/lyrasis/archivesspace-client>.

## License

Expand Down
20 changes: 10 additions & 10 deletions examples/export.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
config = ArchivesSpace::Configuration.new(
{
base_uri: "https://test.archivesspace.org/staff/api",
base_repo: "",
username: "admin",
password: "admin",
page_size: 50,
Expand All @@ -19,17 +18,18 @@

client = ArchivesSpace::Client.new(config).login
client.config.throttle = 0.5
client.config.base_repo = "repositories/2"

begin
# date -d '2021-02-01 00:00:00' +'%s' # 1612166400
client.resources(query: {modified_since: "1612166400"}).each do |resource|
# for now we are just printing ...
# but you would actually write to a zip file or whatever
id = resource["uri"].split("/")[-1]
opts = {include_unpublished: false}
response = client.get("resource_descriptions/#{id}.xml", opts)
puts Nokogiri::XML(response.body).to_xml
client.repository 2 do
# date -d '2021-02-01 00:00:00' +'%s' # 1612166400
client.resources(query: {modified_since: "1612166400"}).each do |resource|
# for now we are just printing ...
# but you would actually write to a zip file or whatever
id = resource["uri"].split("/")[-1]
opts = {include_unpublished: false}
response = client.get("resource_descriptions/#{id}.xml", opts)
puts Nokogiri::XML(response.body).to_xml
end
end
rescue ArchivesSpace::RequestError => e
puts e.message
Expand Down
1 change: 0 additions & 1 deletion examples/repo_and_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
config = ArchivesSpace::Configuration.new(
{
base_uri: "https://sandbox.archivesspace.org/staff/api",
base_repo: "",
username: "admin",
password: "admin",
page_size: 50,
Expand Down
15 changes: 13 additions & 2 deletions examples/test_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
# official sandbox
config = ArchivesSpace::Configuration.new(
{
base_uri: "https://sandbox.archivesspace.org/staff/api",
base_repo: "",
base_uri: "https://test.archivesspace.org/staff/api",
username: "admin",
password: "admin",
page_size: 50,
Expand All @@ -17,4 +16,16 @@
)

client = ArchivesSpace::Client.new(config).login

# globally scoped
puts client.get("version").body
puts client.get("repositories").body
puts client.all("users").map { |u| u["username"] }.to_a

# repo scoped
client.repository 2 do
puts client.get("accessions", query: {page: 1}).parsed["results"].map { |a| a["uri"] }.to_a
end

# globally scoped, full path arg
puts client.get("repositories/2/resources", query: {all_ids: true}).body
1 change: 0 additions & 1 deletion examples/update_feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
config = ArchivesSpace::Configuration.new(
{
base_uri: "https://test.archivesspace.org/staff/api",
base_repo: "",
username: "admin",
password: "admin",
page_size: 50,
Expand Down
8 changes: 4 additions & 4 deletions examples/user_groups.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
config = ArchivesSpace::Configuration.new(
{
base_uri: "https://sandbox.archivesspace.org/staff/api",
base_repo: "",
username: "admin",
password: "admin",
page_size: 50,
Expand All @@ -31,9 +30,10 @@
}

begin
client.config.base_repo = "repositories/2"
results = client.group_user_assignment users_with_roles
puts results.map(&:parsed)
client.repository 2 do
results = client.group_user_assignment users_with_roles
puts results.map(&:parsed)
end
rescue ArchivesSpace::RequestError => e
puts e.message
end
4 changes: 3 additions & 1 deletion lib/archivesspace/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
require "archivesspace/client/cli" # load the registry last

module ArchivesSpace
class ConnectionError < StandardError; end
class AuthenticationError < StandardError; end

class ConfigurationError < StandardError; end

class RepositoryIdError < StandardError; end

Expand Down
31 changes: 20 additions & 11 deletions lib/archivesspace/client/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ class Client
include Task

attr_accessor :token
attr_reader :config
attr_reader :config, :context

NAME = "ArchivesSpaceClient"
TOKEN = "X-ArchivesSpace-Session"

def initialize(config = Configuration.new)
raise "Invalid configuration object" unless config.is_a? ArchivesSpace::Configuration
unless config.is_a? ArchivesSpace::Configuration
raise ConfigurationError, "expected ArchivesSpace::Configuration, got #{config.class}"
end

@config = config
@context = nil
@token = nil
end

Expand All @@ -39,31 +43,36 @@ def delete(path)

# Scoping requests
def repository(id)
if id.nil?
use_global_repository
return
end
return use_global_repository if id.nil?

begin
Integer(id)
rescue ArgumentError, TypeError
raise RepositoryIdError, "Invalid Repository id: #{id}"
end

@config.base_repo = "repositories/#{id}"
new_context = "repositories/#{id}"
return @context = new_context unless block_given?

previous = @context
@context = new_context
begin
yield
ensure
@context = previous
end
end

def use_global_repository
@config.base_repo = ""
@context = nil
end

private

def request(method, path, options = {})
sleep config.throttle
options[:headers] = {"X-ArchivesSpace-Session" => token} if token
result = Request.new(config, method, path, options).execute
Response.new result
options[:headers] = {TOKEN => token} if token
Request.new(context, config, method, path, options).execute
end
end
end
9 changes: 4 additions & 5 deletions lib/archivesspace/client/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

module ArchivesSpace
class Configuration
attr_accessor :base_uri, :base_repo, :debug, :username, :password,
:page_size, :throttle, :verify_ssl, :timeout
attr_accessor :base_uri, :debug, :username, :password,
:page_size, :throttle, :timeout, :verify_ssl

DEFAULTS = {
base_uri: "http://localhost:8089",
base_repo: "",
debug: false,
username: "admin",
password: "admin",
page_size: 50,
throttle: 0,
verify_ssl: true,
timeout: 60
timeout: 60,
verify_ssl: true
}.freeze

def initialize(settings = {})
Expand Down
29 changes: 9 additions & 20 deletions lib/archivesspace/client/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,29 @@ class Request

attr_reader :config, :headers, :method, :path, :options

def default_headers(method = :get)
headers = {
delete: {},
get: {},
post: {
"Content-Type" => "application/json"
},
put: {
"Content-Type" => "application/json"
}
}
headers[method]
end
DEFAULT_HEADERS = {"Content-Type" => "application/json"}.freeze

def initialize(config, method = "GET", path = "", options = {})
def initialize(context, config, method = "GET", path = "", options = {})
@config = config

@method = method.downcase.to_sym
@path = path.gsub(%r{^/+}, "")

@options = options
@options[:headers] =
options[:headers] ? default_headers(@method).merge(options[:headers]) : default_headers(@method)

@options[:headers] = DEFAULT_HEADERS.merge(@options.fetch(:headers, {}))
@options[:headers]["User-Agent"] = "#{Client::NAME}/#{Client::VERSION}"

@options[:verify] = config.verify_ssl
@options[:timeout] = config.timeout
@options[:query] = {} unless options.key? :query

self.class.debug_output($stdout) if @config.debug

base_uri = config.base_repo&.length&.positive? ? File.join(config.base_uri, config.base_repo) : config.base_uri
self.class.base_uri base_uri
self.class.base_uri context ? "#{config.base_uri}/#{context}" : config.base_uri
end

def execute
self.class.send method, "/#{path}", options
Response.new(self.class.send(method, "/#{path}", options))
end
end
end
22 changes: 13 additions & 9 deletions lib/archivesspace/client/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ def group_user_assignment(users_with_roles)
def login
username = config.username
password = config.password
base_repo = config.base_repo
use_global_repository # ensure we're in the global scope to login
result = request("POST", "/users/#{username}/login", {query: {password: password}})
unless result.parsed["session"]
raise ConnectionError, "API client login failed as user [#{username}], check username and password are correct"
end

config.base_repo = base_repo # reset repo as set by the cfg
@token = result.parsed["session"]
self
previous_context = @context
@context = nil
begin
result = request("POST", "/users/#{username}/login", {query: {password: password}})
unless result.parsed["session"]
raise AuthenticationError, "Login failed as user [#{username}] (status #{result.status_code}); check username and password"
end

@token = result.parsed["session"]
self
ensure
@context = previous_context
end
end

def password_reset(username, password)
Expand Down
Loading
Loading