archivesspace-client 0.1.12 → 0.3.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 +4 -4
- data/.github/workflows/ci.yml +5 -4
- data/.github/workflows/publish.yml +1 -1
- data/.rubocop.yml +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +1 -1
- data/README.md +10 -5
- data/Rakefile +5 -3
- data/archivesspace-client.gemspec +25 -29
- data/examples/export.rb +11 -11
- data/examples/password_reset.rb +6 -6
- data/examples/repo_and_user.rb +21 -21
- data/examples/templates.rb +18 -0
- data/examples/test_connection.rb +8 -8
- data/examples/user_groups.rb +14 -14
- data/exe/asclient +2 -2
- data/features/support/setup.rb +3 -3
- data/lib/archivesspace/client/cli/exec.rb +18 -18
- data/lib/archivesspace/client/cli/version.rb +1 -1
- data/lib/archivesspace/client/cli.rb +3 -3
- data/lib/archivesspace/client/client.rb +13 -11
- data/lib/archivesspace/client/configuration.rb +8 -7
- data/lib/archivesspace/client/pagination.rb +3 -3
- data/lib/archivesspace/client/request.rb +13 -11
- data/lib/archivesspace/client/response.rb +5 -5
- data/lib/archivesspace/client/task.rb +15 -15
- data/lib/archivesspace/client/template.rb +53 -10
- data/lib/archivesspace/client/templates/resource.json.jbuilder +7 -0
- data/lib/archivesspace/client/version.rb +1 -1
- data/lib/archivesspace/client.rb +19 -18
- data/spec/archivesspace/client_spec.rb +36 -38
- data/spec/archivesspace/configuration_spec.rb +8 -8
- data/spec/archivesspace/templates_spec.rb +25 -11
- data/spec/fixtures/cassettes/backend_version.yml +35 -24
- data/spec/spec_helper.rb +8 -8
- metadata +46 -92
@@ -4,23 +4,24 @@ module ArchivesSpace
|
|
4
4
|
class Configuration
|
5
5
|
def defaults
|
6
6
|
{
|
7
|
-
base_uri:
|
8
|
-
base_repo:
|
7
|
+
base_uri: "http://localhost:8089",
|
8
|
+
base_repo: "",
|
9
9
|
debug: false,
|
10
|
-
username:
|
11
|
-
password:
|
10
|
+
username: "admin",
|
11
|
+
password: "admin",
|
12
12
|
page_size: 50,
|
13
13
|
throttle: 0,
|
14
|
-
verify_ssl: true
|
14
|
+
verify_ssl: true,
|
15
|
+
timeout: 60
|
15
16
|
}
|
16
17
|
end
|
17
18
|
|
18
19
|
def initialize(settings = {})
|
19
20
|
settings = defaults.merge(settings)
|
20
21
|
settings.each do |property, value|
|
21
|
-
next unless defaults.
|
22
|
+
next unless defaults.key?(property)
|
22
23
|
|
23
|
-
instance_variable_set("@#{property}", value)
|
24
|
+
instance_variable_set(:"@#{property}", value)
|
24
25
|
self.class.send(:attr_accessor, property)
|
25
26
|
end
|
26
27
|
end
|
@@ -20,7 +20,7 @@ module ArchivesSpace
|
|
20
20
|
]
|
21
21
|
|
22
22
|
ENDPOINTS.each do |endpoint|
|
23
|
-
method_name = endpoint.split(
|
23
|
+
method_name = endpoint.split("/").last # remove prefix
|
24
24
|
define_method(method_name) do |options = {}|
|
25
25
|
all(endpoint, options)
|
26
26
|
end
|
@@ -36,8 +36,8 @@ module ArchivesSpace
|
|
36
36
|
result = get(path, options)
|
37
37
|
results = []
|
38
38
|
|
39
|
-
if result.parsed.respond_to?(:key) && result.parsed.key?(
|
40
|
-
results = result.parsed[
|
39
|
+
if result.parsed.respond_to?(:key) && result.parsed.key?("results")
|
40
|
+
results = result.parsed["results"]
|
41
41
|
else
|
42
42
|
results = result.parsed
|
43
43
|
unlimited_listing = true
|
@@ -10,26 +10,28 @@ module ArchivesSpace
|
|
10
10
|
delete: {},
|
11
11
|
get: {},
|
12
12
|
post: {
|
13
|
-
|
14
|
-
|
13
|
+
"Content-Type" => "application/json",
|
14
|
+
"Content-Length" => "nnnn"
|
15
15
|
},
|
16
16
|
put: {
|
17
|
-
|
18
|
-
|
17
|
+
"Content-Type" => "application/json",
|
18
|
+
"Content-Length" => "nnnn"
|
19
19
|
}
|
20
20
|
}
|
21
21
|
headers[method]
|
22
22
|
end
|
23
23
|
|
24
|
-
def initialize(config, method =
|
25
|
-
@config
|
26
|
-
@method
|
27
|
-
@path
|
28
|
-
@options
|
24
|
+
def initialize(config, method = "GET", path = "", options = {})
|
25
|
+
@config = config
|
26
|
+
@method = method.downcase.to_sym
|
27
|
+
@path = path.gsub(%r{^/+}, "")
|
28
|
+
@options = options
|
29
29
|
@options[:headers] =
|
30
30
|
options[:headers] ? default_headers(@method).merge(options[:headers]) : default_headers(@method)
|
31
|
-
@options[:
|
32
|
-
@options[:
|
31
|
+
@options[:headers]["User-Agent"] = "#{Client::NAME}/#{Client::VERSION}"
|
32
|
+
@options[:verify] = config.verify_ssl
|
33
|
+
@options[:timeout] = config.timeout
|
34
|
+
@options[:query] = {} unless options.key? :query
|
33
35
|
|
34
36
|
self.class.debug_output($stdout) if @config.debug
|
35
37
|
|
@@ -5,11 +5,11 @@ module ArchivesSpace
|
|
5
5
|
attr_reader :result, :parsed, :body, :headers, :status, :status_code
|
6
6
|
|
7
7
|
def initialize(result)
|
8
|
-
@result
|
9
|
-
@parsed
|
10
|
-
@body
|
11
|
-
@headers
|
12
|
-
@status
|
8
|
+
@result = result
|
9
|
+
@parsed = result.parsed_response
|
10
|
+
@body = result.body
|
11
|
+
@headers = result.headers
|
12
|
+
@status = result.response
|
13
13
|
@status_code = result.code.to_i
|
14
14
|
end
|
15
15
|
end
|
@@ -10,51 +10,51 @@ module ArchivesSpace
|
|
10
10
|
def group_user_assignment(users_with_roles)
|
11
11
|
updated = []
|
12
12
|
groups.each do |group|
|
13
|
-
group = get("groups/#{uri_to_id(group[
|
13
|
+
group = get("groups/#{uri_to_id(group["uri"])}").parsed
|
14
14
|
update = false
|
15
15
|
|
16
16
|
users_with_roles.each do |user, roles|
|
17
17
|
# should the user still belong to this group?
|
18
|
-
if group[
|
19
|
-
unless roles.include? group[
|
20
|
-
group[
|
18
|
+
if group["member_usernames"].include?(user)
|
19
|
+
unless roles.include? group["group_code"]
|
20
|
+
group["member_usernames"].delete user
|
21
21
|
update = true
|
22
22
|
end
|
23
23
|
# should the user be added to this group?
|
24
|
-
elsif roles.include? group[
|
25
|
-
group[
|
24
|
+
elsif roles.include? group["group_code"]
|
25
|
+
group["member_usernames"] << user
|
26
26
|
update = true
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
next unless update
|
31
31
|
|
32
|
-
response = post("/groups/#{uri_to_id(group[
|
32
|
+
response = post("/groups/#{uri_to_id(group["uri"])}", group.to_json)
|
33
33
|
updated << response
|
34
34
|
end
|
35
35
|
updated
|
36
36
|
end
|
37
37
|
|
38
38
|
def login
|
39
|
-
username
|
40
|
-
password
|
39
|
+
username = config.username
|
40
|
+
password = config.password
|
41
41
|
base_repo = config.base_repo
|
42
42
|
use_global_repository # ensure we're in the global scope to login
|
43
|
-
result = request(
|
44
|
-
unless result.parsed[
|
43
|
+
result = request("POST", "/users/#{username}/login", {query: {password: password}})
|
44
|
+
unless result.parsed["session"]
|
45
45
|
raise ConnectionError, "API client login failed as user [#{username}], check username and password are correct"
|
46
46
|
end
|
47
47
|
|
48
48
|
config.base_repo = base_repo # reset repo as set by the cfg
|
49
|
-
@token = result.parsed[
|
49
|
+
@token = result.parsed["session"]
|
50
50
|
self
|
51
51
|
end
|
52
52
|
|
53
53
|
def password_reset(username, password)
|
54
|
-
user = all(
|
54
|
+
user = all("users").find { |u| u["username"] == username }
|
55
55
|
raise RequestError, user.status unless user
|
56
56
|
|
57
|
-
post(user[
|
57
|
+
post(user["uri"], user.to_json, {password: password})
|
58
58
|
end
|
59
59
|
|
60
60
|
# def search(params)
|
@@ -64,7 +64,7 @@ module ArchivesSpace
|
|
64
64
|
private
|
65
65
|
|
66
66
|
def uri_to_id(uri)
|
67
|
-
uri.split(
|
67
|
+
uri.split("/").last
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -3,24 +3,67 @@
|
|
3
3
|
module ArchivesSpace
|
4
4
|
module Template
|
5
5
|
def self.list
|
6
|
-
Dir.glob File.join(templates_path
|
6
|
+
Dir.glob ["*"], base: File.join(templates_path)
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.process(template, data)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.read(file)
|
16
|
-
File.read("#{templates_path}/#{file}.json.erb")
|
10
|
+
processor = File.extname(template).delete(".").camelize
|
11
|
+
processor = Object.const_get("ArchivesSpace::Template::#{processor}")
|
12
|
+
processor.new(template, data).process
|
17
13
|
end
|
18
14
|
|
19
15
|
def self.templates_path
|
20
16
|
ENV.fetch(
|
21
|
-
|
22
|
-
File.join(File.dirname(File.expand_path(__FILE__)),
|
17
|
+
"ARCHIVESSPACE_CLIENT_TEMPLATES_PATH",
|
18
|
+
File.join(File.dirname(File.expand_path(__FILE__)), "templates")
|
23
19
|
)
|
24
20
|
end
|
21
|
+
|
22
|
+
class Processor
|
23
|
+
attr_reader :template, :data
|
24
|
+
|
25
|
+
def initialize(template, data)
|
26
|
+
@template = template
|
27
|
+
@data = data
|
28
|
+
|
29
|
+
validate_template
|
30
|
+
end
|
31
|
+
|
32
|
+
def extension
|
33
|
+
raise "Not implemented"
|
34
|
+
end
|
35
|
+
|
36
|
+
def read_template
|
37
|
+
File.read(File.join(ArchivesSpace::Template.templates_path, template))
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_template
|
41
|
+
raise "Invalid template" unless File.extname(template).end_with? extension
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Erb < Processor
|
46
|
+
def extension
|
47
|
+
".erb"
|
48
|
+
end
|
49
|
+
|
50
|
+
def process
|
51
|
+
t = ERB.new(read_template)
|
52
|
+
r = t.result(binding).squeeze("\n")
|
53
|
+
JSON.parse(r).to_json
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Jbuilder < Processor
|
58
|
+
def extension
|
59
|
+
".jbuilder"
|
60
|
+
end
|
61
|
+
|
62
|
+
def process
|
63
|
+
::Jbuilder.encode do |json|
|
64
|
+
eval(read_template, binding) # standard:disable Security/Eval
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
25
68
|
end
|
26
69
|
end
|
data/lib/archivesspace/client.rb
CHANGED
@@ -1,34 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "dry/cli"
|
4
|
+
require "httparty"
|
5
|
+
require "json"
|
6
|
+
require "nokogiri"
|
7
|
+
require "jbuilder"
|
7
8
|
|
8
9
|
# mixins required first
|
9
|
-
require
|
10
|
-
require
|
10
|
+
require "archivesspace/client/pagination"
|
11
|
+
require "archivesspace/client/task"
|
11
12
|
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
13
|
+
require "archivesspace/client/client"
|
14
|
+
require "archivesspace/client/configuration"
|
15
|
+
require "archivesspace/client/request"
|
16
|
+
require "archivesspace/client/response"
|
17
|
+
require "archivesspace/client/template"
|
18
|
+
require "archivesspace/client/version"
|
18
19
|
|
19
20
|
# cli
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
21
|
+
require "archivesspace/client/cli/exec"
|
22
|
+
require "archivesspace/client/cli/version"
|
23
|
+
require "archivesspace/client/cli" # load the registry last
|
23
24
|
|
24
25
|
module ArchivesSpace
|
25
26
|
class ConnectionError < RuntimeError; end
|
26
27
|
|
27
|
-
class ContextError
|
28
|
+
class ContextError < RuntimeError; end
|
28
29
|
|
29
30
|
class RepositoryIdError < RuntimeError; end
|
30
31
|
|
31
|
-
class ParamsError
|
32
|
+
class ParamsError < RuntimeError; end
|
32
33
|
|
33
|
-
class RequestError
|
34
|
+
class RequestError < RuntimeError; end
|
34
35
|
end
|
@@ -1,87 +1,85 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "spec_helper"
|
4
4
|
|
5
5
|
describe ArchivesSpace::Client do
|
6
6
|
let(:client) { ArchivesSpace::Client.new }
|
7
|
-
let(:login) { -> { client.login } }
|
8
7
|
|
9
|
-
describe
|
10
|
-
it
|
11
|
-
client = ArchivesSpace::Client.new
|
8
|
+
describe "Configuration" do
|
9
|
+
it "will use the default configuration if none is provided" do
|
12
10
|
expect(client.config.base_uri).to eq DEFAULT_BASE_URI
|
13
11
|
end
|
14
12
|
|
15
|
-
it
|
16
|
-
expect { ArchivesSpace::Client.new({
|
13
|
+
it "will raise an error if supplied configuration is of invalid type" do
|
14
|
+
expect { ArchivesSpace::Client.new({base_uri: CUSTOM_BASE_URI}) }.to raise_error(RuntimeError)
|
17
15
|
end
|
18
16
|
|
19
|
-
it
|
20
|
-
client = ArchivesSpace::Client.new(ArchivesSpace::Configuration.new({
|
17
|
+
it "will allow a configuration object to be provided" do
|
18
|
+
client = ArchivesSpace::Client.new(ArchivesSpace::Configuration.new({base_uri: CUSTOM_BASE_URI}))
|
21
19
|
expect(client.config.base_uri).to eq CUSTOM_BASE_URI
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
25
|
-
describe
|
26
|
-
it
|
27
|
-
client = ArchivesSpace::Client.new
|
23
|
+
describe "Repository scoping" do
|
24
|
+
it "will set the repository with an integer id" do
|
28
25
|
client.repository 2
|
29
|
-
expect(client.config.base_repo).to eq
|
26
|
+
expect(client.config.base_repo).to eq "repositories/2"
|
30
27
|
end
|
31
28
|
|
32
|
-
it
|
33
|
-
client
|
34
|
-
client.
|
35
|
-
expect(client.config.base_repo).to eq 'repositories/2'
|
29
|
+
it "will set the repository with a string id cast to integer" do
|
30
|
+
client.repository "2"
|
31
|
+
expect(client.config.base_repo).to eq "repositories/2"
|
36
32
|
end
|
37
33
|
|
38
|
-
it
|
39
|
-
client
|
40
|
-
expect { client.repository('xyz') }.to raise_error(
|
34
|
+
it "will fail if the id cannot be cast to integer" do
|
35
|
+
expect { client.repository("xyz") }.to raise_error(
|
41
36
|
ArchivesSpace::RepositoryIdError
|
42
37
|
)
|
43
38
|
end
|
44
39
|
|
45
|
-
it
|
46
|
-
client = ArchivesSpace::Client.new
|
40
|
+
it "will use the global repo if repository is passed nil" do
|
47
41
|
client.repository 2
|
48
42
|
client.repository nil
|
49
|
-
expect(client.config.base_repo).to eq
|
43
|
+
expect(client.config.base_repo).to eq ""
|
50
44
|
end
|
51
45
|
|
52
|
-
it
|
53
|
-
client = ArchivesSpace::Client.new
|
46
|
+
it "will use the global repo when the method is called" do
|
54
47
|
client.repository 2
|
55
48
|
client.use_global_repository
|
56
|
-
expect(client.config.base_repo).to eq
|
49
|
+
expect(client.config.base_repo).to eq ""
|
57
50
|
end
|
58
51
|
end
|
59
52
|
|
60
|
-
describe
|
61
|
-
it
|
62
|
-
|
53
|
+
describe "Requests" do
|
54
|
+
it "will have an identifiable user agent" do
|
55
|
+
request = ArchivesSpace::Request.new(client.config)
|
56
|
+
expect(request.options[:headers]["User-Agent"]).to eq "#{ArchivesSpace::Client::NAME}/#{ArchivesSpace::Client::VERSION}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "Pagination" do
|
61
|
+
it "will have a method for defined paginated record types" do
|
63
62
|
ArchivesSpace::Pagination::ENDPOINTS.each do |e|
|
64
|
-
next if e.match?(
|
63
|
+
next if e.match?("/")
|
65
64
|
|
66
65
|
expect(client.respond_to?(e.to_sym)).to be true
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
70
|
-
it
|
71
|
-
client = ArchivesSpace::Client.new
|
69
|
+
it "will have a method for defined paginated record types with multipart path" do
|
72
70
|
expect(client.respond_to?(:people)).to be true
|
73
71
|
end
|
74
72
|
end
|
75
73
|
|
76
|
-
describe
|
77
|
-
it
|
74
|
+
describe "Version information" do
|
75
|
+
it "has a version number" do
|
78
76
|
expect(ArchivesSpace::Client::VERSION).not_to be nil
|
79
77
|
end
|
80
78
|
|
81
|
-
it
|
82
|
-
VCR.use_cassette(
|
83
|
-
login
|
84
|
-
response = client.get
|
79
|
+
it "can retrieve the backend version info" do
|
80
|
+
VCR.use_cassette("backend_version") do
|
81
|
+
client.login
|
82
|
+
response = client.get "version"
|
85
83
|
expect(response.status_code).to eq(200)
|
86
84
|
expect(response.body).to match(/ArchivesSpace \(.*\)/)
|
87
85
|
end
|
@@ -1,28 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "spec_helper"
|
4
4
|
|
5
5
|
describe ArchivesSpace::Configuration do
|
6
|
-
it
|
6
|
+
it "uses the default profile for configuration settings" do
|
7
7
|
config = ArchivesSpace::Configuration.new
|
8
8
|
expect(config.base_uri).to eq DEFAULT_BASE_URI
|
9
9
|
end
|
10
10
|
|
11
|
-
it
|
11
|
+
it "allows configuration settings to be provided" do
|
12
12
|
config = ArchivesSpace::Configuration.new({
|
13
|
-
|
14
|
-
|
13
|
+
base_uri: CUSTOM_BASE_URI
|
14
|
+
})
|
15
15
|
expect(config.base_uri).to eq CUSTOM_BASE_URI
|
16
16
|
end
|
17
17
|
|
18
|
-
it
|
18
|
+
it "allows the configuration properties to be updated" do
|
19
19
|
config = ArchivesSpace::Configuration.new
|
20
20
|
config.base_uri = CUSTOM_BASE_URI
|
21
21
|
expect(config.base_uri).to eq CUSTOM_BASE_URI
|
22
22
|
end
|
23
23
|
|
24
|
-
it
|
25
|
-
config = ArchivesSpace::Configuration.new({
|
24
|
+
it "ignores unrecognized configuration properties" do
|
25
|
+
config = ArchivesSpace::Configuration.new({xyz: 123})
|
26
26
|
expect { config.xyz }.to raise_error(NoMethodError)
|
27
27
|
end
|
28
28
|
end
|
@@ -1,26 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "spec_helper"
|
4
4
|
|
5
5
|
describe ArchivesSpace::Template do
|
6
|
-
it
|
6
|
+
it "can list the default templates" do
|
7
7
|
templates = ArchivesSpace::Template.list
|
8
8
|
expect(templates).to_not be_empty
|
9
9
|
expect(templates).to include(/repository_with_agent.*erb/)
|
10
|
+
expect(templates).to include(/resource.*jbuilder/)
|
10
11
|
end
|
11
12
|
|
12
|
-
it
|
13
|
+
it "can change the path when template envvar is set" do
|
13
14
|
expect(ArchivesSpace::Template.templates_path).to match(
|
14
|
-
/#{File.join(
|
15
|
+
/#{File.join("lib", "archivesspace", "client", "templates")}/
|
15
16
|
)
|
16
|
-
ENV[
|
17
|
-
expect(ArchivesSpace::Template.templates_path).to eq
|
18
|
-
ENV.delete(
|
17
|
+
ENV["ARCHIVESSPACE_CLIENT_TEMPLATES_PATH"] = "/path/to/nowhere"
|
18
|
+
expect(ArchivesSpace::Template.templates_path).to eq "/path/to/nowhere"
|
19
|
+
ENV.delete("ARCHIVESSPACE_CLIENT_TEMPLATES_PATH")
|
19
20
|
end
|
20
21
|
|
21
|
-
it
|
22
|
-
data = {
|
23
|
-
json = JSON.parse(ArchivesSpace::Template.process(
|
24
|
-
expect(json[
|
22
|
+
it "can process an erb template" do
|
23
|
+
data = {repo_code: "ABC", name: "ABC Archive", agent_contact_name: "ABC Admin"}
|
24
|
+
json = JSON.parse(ArchivesSpace::Template.process("repository_with_agent.json.erb", data))
|
25
|
+
expect(json["repository"]["repo_code"]).to eq data[:repo_code]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can process a jbuilder template" do
|
29
|
+
data = {"title" => "Title", "object_number" => "001.001", "description_level" => "collection"}
|
30
|
+
json = JSON.parse(ArchivesSpace::Template.process("resource.json.jbuilder", data))
|
31
|
+
expect(json["id_0"]).to eq data["object_number"]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "rejects a template that does not match by extension" do
|
35
|
+
data = {"title" => "Title"}
|
36
|
+
expect {
|
37
|
+
JSON.parse(ArchivesSpace::Template::Erb.new("resource.json.jbuilder", data).process)
|
38
|
+
}.to raise_error "Invalid template"
|
25
39
|
end
|
26
40
|
end
|