archivesspace-client 0.1.12 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|