archivesspace-client 0.1.6 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +1,17 @@
1
- module ArchivesSpace
1
+ # frozen_string_literal: true
2
2
 
3
+ module ArchivesSpace
3
4
  class Configuration
4
-
5
5
  def defaults
6
6
  {
7
- base_uri: "http://localhost:8089",
8
- base_repo: "",
9
- username: "admin",
10
- password: "admin",
7
+ base_uri: 'http://localhost:8089',
8
+ base_repo: '',
9
+ debug: false,
10
+ username: 'admin',
11
+ password: 'admin',
11
12
  page_size: 50,
12
13
  throttle: 0,
13
- verify_ssl: true,
14
+ verify_ssl: true
14
15
  }
15
16
  end
16
17
 
@@ -18,11 +19,10 @@ module ArchivesSpace
18
19
  settings = defaults.merge(settings)
19
20
  settings.each do |property, value|
20
21
  next unless defaults.keys.include? property
22
+
21
23
  instance_variable_set("@#{property}", value)
22
24
  self.class.send(:attr_accessor, property)
23
25
  end
24
26
  end
25
-
26
27
  end
27
-
28
- end
28
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArchivesSpace
4
+ # Handle API Pagination using enumerator
5
+ module Pagination
6
+ # TODO: get via lookup of endpoints that support pagination? (nice-to-have)
7
+ ENDPOINTS = %w[
8
+ accessions
9
+ agents/corporate_entities
10
+ agents/families
11
+ agents/people
12
+ agents/software
13
+ archival_objects
14
+ digital_objects
15
+ groups
16
+ repositiories
17
+ resources
18
+ subjects
19
+ users
20
+ ]
21
+
22
+ ENDPOINTS.each do |endpoint|
23
+ method_name = endpoint.split('/').last # remove prefix
24
+ define_method(method_name) do |options = {}|
25
+ all(endpoint, options)
26
+ end
27
+ end
28
+
29
+ def all(path, options = {})
30
+ Enumerator.new do |yielder|
31
+ page = 1
32
+ unlimited_listing = false
33
+ loop do
34
+ options[:query] ||= {}
35
+ options[:query][:page] = page
36
+ result = get(path, options)
37
+ results = []
38
+
39
+ if result.parsed.respond_to?(:key) && result.parsed.key?('results')
40
+ results = result.parsed['results']
41
+ else
42
+ results = result.parsed
43
+ unlimited_listing = true
44
+ end
45
+
46
+ if results.any?
47
+ results.each do |i|
48
+ yielder << i
49
+ end
50
+ raise StopIteration if unlimited_listing
51
+
52
+ page += 1
53
+ else
54
+ raise StopIteration
55
+ end
56
+ end
57
+ end.lazy
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,6 @@
1
- module ArchivesSpace
1
+ # frozen_string_literal: true
2
2
 
3
+ module ArchivesSpace
3
4
  class Request
4
5
  include HTTParty
5
6
  attr_reader :config, :headers, :method, :path, :options
@@ -9,37 +10,34 @@ module ArchivesSpace
9
10
  delete: {},
10
11
  get: {},
11
12
  post: {
12
- "Content-Type" => "application/json",
13
- "Content-Length" => "nnnn",
13
+ 'Content-Type' => 'application/json',
14
+ 'Content-Length' => 'nnnn'
14
15
  },
15
16
  put: {
16
- "Content-Type" => "application/json",
17
- "Content-Length" => "nnnn",
17
+ 'Content-Type' => 'application/json',
18
+ 'Content-Length' => 'nnnn'
18
19
  }
19
20
  }
20
21
  headers[method]
21
22
  end
22
23
 
23
- def initialize(config, method = "GET", path = "", options = {})
24
+ def initialize(config, method = 'GET', path = '', options = {})
24
25
  @config = config
25
26
  @method = method.downcase.to_sym
26
- @path = path.gsub(/^\/+/, '')
27
+ @path = path.gsub(%r{^/+}, '')
27
28
  @options = options
28
29
  @options[:headers] = options[:headers] ? default_headers(@method).merge(options[:headers]) : default_headers(@method)
29
30
  @options[:verify] = config.verify_ssl
30
- @options[:query] = {} unless options.has_key? :query
31
+ @options[:query] = {} unless options.key? :query
31
32
 
32
- base_uri = (
33
- config.base_repo.nil? or config.base_repo.empty?
34
- ) ? config.base_uri : "#{config.base_uri}/#{config.base_repo}"
33
+ self.class.debug_output($stdout) if @config.debug
35
34
 
35
+ base_uri = config.base_repo&.length&.positive? ? File.join(config.base_uri, config.base_repo) : config.base_uri
36
36
  self.class.base_uri base_uri
37
37
  end
38
38
 
39
39
  def execute
40
40
  self.class.send method, "/#{path}", options
41
41
  end
42
-
43
42
  end
44
-
45
43
  end
@@ -1,10 +1,10 @@
1
- module ArchivesSpace
1
+ # frozen_string_literal: true
2
2
 
3
+ module ArchivesSpace
3
4
  class Response
4
- attr_reader :result, :parsed, :body, :headers, :status, :status_code, :xml
5
+ attr_reader :result, :parsed, :body, :headers, :status, :status_code
5
6
 
6
7
  def initialize(result)
7
- # throw error
8
8
  @result = result
9
9
  @parsed = result.parsed_response
10
10
  @body = result.body
@@ -12,7 +12,5 @@ module ArchivesSpace
12
12
  @status = result.response
13
13
  @status_code = result.code.to_i
14
14
  end
15
-
16
15
  end
17
-
18
- end
16
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArchivesSpace
4
+ # Perform specific API tasks
5
+ module Task
6
+ # def batch_import(payload, params = {})
7
+ # # TODO: create "batch_import", payload, params
8
+ # end
9
+
10
+ def group_user_assignment(users_with_roles)
11
+ updated = []
12
+ groups.each do |group|
13
+ group = get("groups/#{uri_to_id(group['uri'])}").parsed
14
+ update = false
15
+
16
+ users_with_roles.each do |user, roles|
17
+ # should the user still belong to this group?
18
+ if group['member_usernames'].include?(user)
19
+ unless roles.include? group['group_code']
20
+ group['member_usernames'].delete user
21
+ update = true
22
+ end
23
+ # should the user be added to this group?
24
+ elsif roles.include? group['group_code']
25
+ group['member_usernames'] << user
26
+ update = true
27
+ end
28
+ end
29
+
30
+ next unless update
31
+
32
+ response = post("/groups/#{uri_to_id(group['uri'])}", group.to_json)
33
+ updated << response
34
+ end
35
+ updated
36
+ end
37
+
38
+ def login
39
+ username = config.username
40
+ password = config.password
41
+ base_repo = config.base_repo
42
+ use_global_repository # ensure we're in the global scope to login
43
+ result = request('POST', "/users/#{username}/login", { query: { password: password } })
44
+ unless result.parsed['session']
45
+ raise ConnectionError, "API client login failed as user [#{username}], check username and password are correct"
46
+ end
47
+
48
+ config.base_repo = base_repo # reset repo as set by the cfg
49
+ @token = result.parsed['session']
50
+ self
51
+ end
52
+
53
+ def password_reset(username, password)
54
+ user = all('users').find { |u| u['username'] == username }
55
+ raise RequestError, user.status unless user
56
+
57
+ post(user['uri'], user.to_json, { password: password })
58
+ end
59
+
60
+ # def search(params)
61
+ # # TODO: get "search", params
62
+ # end
63
+
64
+ private
65
+
66
+ def uri_to_id(uri)
67
+ uri.split('/').last
68
+ end
69
+ end
70
+ end
@@ -1,25 +1,26 @@
1
- module ArchivesSpace
1
+ # frozen_string_literal: true
2
2
 
3
+ module ArchivesSpace
3
4
  module Template
4
-
5
5
  def self.list
6
- []
6
+ Dir.glob File.join(templates_path, '*.erb')
7
7
  end
8
8
 
9
- def self.process_template(template, data)
10
- t = ERB.new(self.read_template(template))
11
- r = t.result(binding).gsub(/\n+/,"\n")
12
- JSON.parse(r)
9
+ def self.process(template, data)
10
+ t = ERB.new(read(template))
11
+ r = t.result(binding).gsub(/\n+/, "\n")
12
+ JSON.parse(r).to_json
13
13
  end
14
14
 
15
- def self.read_template(file)
16
- File.read("#{self.templates_path}/#{file.to_s}.json.erb")
15
+ def self.read(file)
16
+ File.read("#{templates_path}/#{file}.json.erb")
17
17
  end
18
18
 
19
19
  def self.templates_path
20
- File.join(File.dirname(File.expand_path(__FILE__)), 'templates')
20
+ ENV.fetch(
21
+ 'ARCHIVESSPACE_CLIENT_TEMPLATES_PATH',
22
+ File.join(File.dirname(File.expand_path(__FILE__)), 'templates')
23
+ )
21
24
  end
22
-
23
25
  end
24
-
25
- end
26
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ArchivesSpace
2
4
  class Client
3
- VERSION = "0.1.6"
5
+ VERSION = '0.1.11'
4
6
  end
5
7
  end
@@ -1,22 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
1
4
  require 'httparty'
2
5
  require 'json'
3
6
  require 'nokogiri'
4
7
 
5
8
  # mixins required first
6
- require "archivesspace/client/helpers"
9
+ require 'archivesspace/client/pagination'
10
+ require 'archivesspace/client/task'
11
+
12
+ require 'archivesspace/client/client'
13
+ require 'archivesspace/client/configuration'
14
+ require 'archivesspace/client/request'
15
+ require 'archivesspace/client/response'
16
+ require 'archivesspace/client/template'
17
+ require 'archivesspace/client/version'
7
18
 
8
- require "archivesspace/client/client"
9
- require "archivesspace/client/configuration"
10
- require "archivesspace/client/request"
11
- require "archivesspace/client/response"
12
- require "archivesspace/client/template"
13
- require "archivesspace/client/version"
19
+ # cli
20
+ require 'archivesspace/client/cli/exec'
21
+ require 'archivesspace/client/cli/version'
22
+ require 'archivesspace/client/cli' # load the registry last
14
23
 
15
24
  module ArchivesSpace
25
+ class ConnectionError < RuntimeError; end
26
+
27
+ class ContextError < RuntimeError; end
28
+
29
+ class RepositoryIdError < RuntimeError; end
16
30
 
17
- class ConnectionError < Exception ; end
18
- class ContextError < Exception ; end
19
- class ParamsError < Exception ; end
20
- class RequestError < Exception ; end
31
+ class ParamsError < RuntimeError; end
21
32
 
22
- end
33
+ class RequestError < RuntimeError; end
34
+ end
@@ -1,43 +1,90 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe ArchivesSpace::Client do
4
-
5
6
  let(:client) { ArchivesSpace::Client.new }
6
7
  let(:login) { -> { client.login } }
7
8
 
8
- describe "Configuration" do
9
-
9
+ describe 'Configuration' do
10
10
  it 'will use the default configuration if none is provided' do
11
11
  client = ArchivesSpace::Client.new
12
12
  expect(client.config.base_uri).to eq DEFAULT_BASE_URI
13
13
  end
14
14
 
15
15
  it 'will raise an error if supplied configuration is of invalid type' do
16
- expect{ ArchivesSpace::Client.new({ base_uri: CUSTOM_BASE_URI }) }.to raise_error(RuntimeError)
16
+ expect { ArchivesSpace::Client.new({ base_uri: CUSTOM_BASE_URI }) }.to raise_error(RuntimeError)
17
17
  end
18
18
 
19
19
  it 'will allow a configuration object to be provided' do
20
20
  client = ArchivesSpace::Client.new(ArchivesSpace::Configuration.new({ base_uri: CUSTOM_BASE_URI }))
21
21
  expect(client.config.base_uri).to eq CUSTOM_BASE_URI
22
22
  end
23
+ end
24
+
25
+ describe 'Repository scoping' do
26
+ it 'will set the repository with an integer id' do
27
+ client = ArchivesSpace::Client.new
28
+ client.repository 2
29
+ expect(client.config.base_repo).to eq 'repositories/2'
30
+ end
31
+
32
+ it 'will set the repository with a string id cast to integer' do
33
+ client = ArchivesSpace::Client.new
34
+ client.repository '2'
35
+ expect(client.config.base_repo).to eq 'repositories/2'
36
+ end
37
+
38
+ it 'will fail if the id cannot be cast to integer' do
39
+ client = ArchivesSpace::Client.new
40
+ expect { client.repository('xyz') }.to raise_error(
41
+ ArchivesSpace::RepositoryIdError
42
+ )
43
+ end
44
+
45
+ it 'will use the global repo if repository is passed nil' do
46
+ client = ArchivesSpace::Client.new
47
+ client.repository 2
48
+ client.repository nil
49
+ expect(client.config.base_repo).to eq ''
50
+ end
23
51
 
52
+ it 'will use the global repo when the method is called' do
53
+ client = ArchivesSpace::Client.new
54
+ client.repository 2
55
+ client.use_global_repository
56
+ expect(client.config.base_repo).to eq ''
57
+ end
24
58
  end
25
59
 
26
- describe "Version information" do
60
+ describe 'Pagination' do
61
+ it 'will have a method for defined paginated record types' do
62
+ client = ArchivesSpace::Client.new
63
+ ArchivesSpace::Pagination::ENDPOINTS.each do |e|
64
+ next if e.match?('/')
65
+
66
+ expect(client.respond_to?(e.to_sym)).to be true
67
+ end
68
+ end
69
+
70
+ it 'will have a method for defined paginated record types with multipart path' do
71
+ client = ArchivesSpace::Client.new
72
+ expect(client.respond_to?(:people)).to be true
73
+ end
74
+ end
27
75
 
76
+ describe 'Version information' do
28
77
  it 'has a version number' do
29
78
  expect(ArchivesSpace::Client::VERSION).not_to be nil
30
79
  end
31
80
 
32
- it "can retrieve the backend version info" do
81
+ it 'can retrieve the backend version info' do
33
82
  VCR.use_cassette('backend_version') do
34
83
  login.call
35
- response = client.get "version"
84
+ response = client.get 'version'
36
85
  expect(response.status_code).to eq(200)
37
86
  expect(response.body).to match(/ArchivesSpace \(.*\)/)
38
87
  end
39
88
  end
40
-
41
89
  end
42
-
43
- end
90
+ end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe ArchivesSpace::Configuration do
4
-
5
6
  it 'uses the default profile for configuration settings' do
6
7
  config = ArchivesSpace::Configuration.new
7
8
  expect(config.base_uri).to eq DEFAULT_BASE_URI
@@ -9,8 +10,8 @@ describe ArchivesSpace::Configuration do
9
10
 
10
11
  it 'allows configuration settings to be provided' do
11
12
  config = ArchivesSpace::Configuration.new({
12
- base_uri: CUSTOM_BASE_URI,
13
- })
13
+ base_uri: CUSTOM_BASE_URI
14
+ })
14
15
  expect(config.base_uri).to eq CUSTOM_BASE_URI
15
16
  end
16
17
 
@@ -22,7 +23,6 @@ describe ArchivesSpace::Configuration do
22
23
 
23
24
  it 'ignores unrecognized configuration properties' do
24
25
  config = ArchivesSpace::Configuration.new({ xyz: 123 })
25
- expect{ config.xyz }.to raise_error(NoMethodError)
26
+ expect { config.xyz }.to raise_error(NoMethodError)
26
27
  end
27
-
28
- end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ArchivesSpace::Template do
6
+ it 'can list the default templates' do
7
+ templates = ArchivesSpace::Template.list
8
+ expect(templates).to_not be_empty
9
+ expect(templates).to include(/repository_with_agent.*erb/)
10
+ end
11
+
12
+ it 'can change the path when template envvar is set' do
13
+ expect(ArchivesSpace::Template.templates_path).to match(
14
+ /#{File.join('lib', 'archivesspace', 'client', 'templates')}/
15
+ )
16
+ ENV['ARCHIVESSPACE_CLIENT_TEMPLATES_PATH'] = '/path/to/nowhere'
17
+ expect(ArchivesSpace::Template.templates_path).to eq '/path/to/nowhere'
18
+ ENV.delete('ARCHIVESSPACE_CLIENT_TEMPLATES_PATH')
19
+ end
20
+
21
+ it 'can process a template' do
22
+ data = { repo_code: 'ABC', name: 'ABC Archive', agent_contact_name: 'ABC Admin' }
23
+ json = JSON.parse(ArchivesSpace::Template.process(:repository_with_agent, data))
24
+ expect(json['repository']['repo_code']).to eq data[:repo_code]
25
+ end
26
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,14 +1,16 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
2
4
  require 'archivesspace/client'
3
5
  require 'vcr'
4
6
  require 'webmock/rspec'
5
7
 
6
8
  # GLOBAL VALUES FOR SPECS
7
- DEFAULT_BASE_URI = "http://localhost:8089"
8
- CUSTOM_BASE_URI = "https://archives.university.edu/api"
9
+ DEFAULT_BASE_URI = 'http://localhost:8089'
10
+ CUSTOM_BASE_URI = 'https://archives.university.edu/api'
9
11
 
10
12
  VCR.configure do |c|
11
- c.cassette_library_dir = "spec/fixtures/cassettes"
13
+ c.cassette_library_dir = 'spec/fixtures/cassettes'
12
14
  c.hook_into :webmock
13
- c.default_cassette_options = { :record => :once }
14
- end
15
+ c.default_cassette_options = { record: :once }
16
+ end