sdr-client 0.17.2 → 0.22.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49708bd983a0e4e3322f7a1449d18bd2836f42020fb0c4fec06cf6ce25ec78a5
4
- data.tar.gz: a6aecda31d1791026f321147514f7d1ec36a5080aa0df3e4e7638c10125de1f5
3
+ metadata.gz: 52457756d6413fcbb3d78c6778e705dd000f73d4bfd9be51b4455c91f861f2cf
4
+ data.tar.gz: 2e3fbb925f332ddbf6f70a011f708ed659bb768bf3cea618e3b40cea70b7e1a8
5
5
  SHA512:
6
- metadata.gz: 3238ad6cbb4ade491ff443448b46f9ee78d081b233a07678046421b1de2a80108074428ccb6a8a768d4cbf1af4a60b14dfb37b8336c83ded75dd04c2cb8a743d
7
- data.tar.gz: cf082df7b2efe67c88349644545495d3555206efb6aca5a2eca156ab144db72a509c9a0c5aa1b69a7d71504c8c14bca9fa75b57dcc5c7cd37a00902387d0f73d
6
+ metadata.gz: cdaa5ce7b1c83361d33911f02191d4a34c97c24113ffc01e4594a2300b966ef7b0ff399ea56ee033650be0eb808dd3a363dbf04357c2e124ebfe378d78a69459
7
+ data.tar.gz: 238c7ff0047034134d1a3c4421bed157e09b97692ec6d57226edfc2e4e5ef0a7be3a88a39b9c10f816ae6e53dc7f44d10dea7d38a1f7424bb9a6a5c9b3ea7fd2
@@ -9,6 +9,8 @@ AllCops:
9
9
 
10
10
  Layout/LineLength:
11
11
  Max: 120
12
+ Exclude:
13
+ - 'spec/sdr_client/deposit/process_spec.rb'
12
14
 
13
15
  Metrics/BlockLength:
14
16
  Exclude:
data/README.md CHANGED
@@ -21,8 +21,15 @@ Log in:
21
21
  sdr --service-url http://sdr-api-server:3000 login
22
22
  ```
23
23
 
24
+ Register a new object:
25
+ ```
26
+ sdr --service-url https://sdr-api-server:3000 register --label 'hey there' \
27
+ --admin-policy 'druid:bk123gh4567' \
28
+ --collection 'druid:gh456kw9876' \
29
+ --source-id 'googlebooks:stanford_12345' file1.png file2.png
30
+ ```
24
31
 
25
- Deposit a new object:
32
+ Deposit (register + accession) a new object:
26
33
  ```
27
34
  sdr --service-url https://sdr-api-server:3000 deposit --label 'hey there' \
28
35
  --admin-policy 'druid:bk123gh4567' \
data/exe/sdr CHANGED
@@ -27,7 +27,10 @@ global = OptionParser.new do |opts|
27
27
 
28
28
  COMMANDS
29
29
  deposit
30
- deposit files to the SDR
30
+ accession object into the SDR
31
+
32
+ register
33
+ create a draft object in SDR
31
34
 
32
35
  login
33
36
  Will prompt for email & password and exchange it for an login token, which it saves in ~/.sdr/token
@@ -40,77 +43,80 @@ end
40
43
  global.order!
41
44
  command = ARGV.shift
42
45
 
43
- subcommands = {
44
- 'deposit' => OptionParser.new do |opts|
45
- opts.on('--label LABEL', 'The object label') do |label|
46
- options[:label] = label
47
- end
46
+ deposit_options = OptionParser.new do |opts|
47
+ opts.on('--label LABEL', 'The object label') do |label|
48
+ options[:label] = label
49
+ end
48
50
 
49
- opts.on('--admin-policy ADMIN_POLICY', 'The druid identifier of the admin policy object') do |apo|
50
- options[:apo] = apo
51
- end
51
+ opts.on('--admin-policy ADMIN_POLICY', 'The druid identifier of the admin policy object') do |apo|
52
+ options[:apo] = apo
53
+ end
52
54
 
53
- opts.on('--type TYPE', 'The object type to create. ' \
54
- 'One of: "image", "book", "document", "map", "manuscript", "media", ' \
55
- '"three_dimensional", "collection", or "admin_policy"') do |type|
56
- if %w[image book document map manuscript media three_dimensional collection admin_policy].include?(type)
57
- options[:type] = "http://cocina.sul.stanford.edu/models/#{type}.jsonld"
58
- end
55
+ opts.on('--type TYPE', 'The object type to create. ' \
56
+ 'One of: "image", "book", "document", "map", "manuscript", "media", ' \
57
+ '"three_dimensional", "collection", or "admin_policy"') do |type|
58
+ if %w[image book document map manuscript media three_dimensional collection admin_policy].include?(type)
59
+ options[:type] = "http://cocina.sul.stanford.edu/models/#{type}.jsonld"
59
60
  end
61
+ end
60
62
 
61
- opts.on('--collection COLLECTION', 'The druid identifier of the collection object') do |collection|
62
- options[:collection] = collection
63
- end
63
+ opts.on('--collection COLLECTION', 'The druid identifier of the collection object') do |collection|
64
+ options[:collection] = collection
65
+ end
64
66
 
65
- opts.on('--catkey CATKEY', 'The catkey for this item') do |catkey|
66
- options[:catkey] = catkey
67
- end
67
+ opts.on('--catkey CATKEY', 'The catkey for this item') do |catkey|
68
+ options[:catkey] = catkey
69
+ end
68
70
 
69
- opts.on('--source-id SOURCE_ID', 'The source id for this object') do |source_id|
70
- options[:source_id] = source_id
71
- end
71
+ opts.on('--source-id SOURCE_ID', 'The source id for this object') do |source_id|
72
+ options[:source_id] = source_id
73
+ end
72
74
 
73
- opts.on('--copyright COPYRIGHT', 'The copyright statement') do |copyright|
74
- options[:copyright] = copyright
75
- end
75
+ opts.on('--copyright COPYRIGHT', 'The copyright statement') do |copyright|
76
+ options[:copyright] = copyright
77
+ end
76
78
 
77
- opts.on('--use-statement STATEMENT', 'The use and reproduction statement') do |use_statement|
78
- options[:use_statement] = use_statement
79
- end
79
+ opts.on('--use-statement STATEMENT', 'The use and reproduction statement') do |use_statement|
80
+ options[:use_statement] = use_statement
81
+ end
80
82
 
81
- opts.on('--viewing-direction DIRECTION', 'The viewing direction (if a book). ' \
82
- 'Either "left-to-right" or "right-to-left"') do |viewing_direction|
83
- options[:viewing_direction] = viewing_direction if %w[left-to-right right-to-left].include?(viewing_direction)
84
- end
83
+ opts.on('--viewing-direction DIRECTION', 'The viewing direction (if a book). ' \
84
+ 'Either "left-to-right" or "right-to-left"') do |viewing_direction|
85
+ options[:viewing_direction] = viewing_direction if %w[left-to-right right-to-left].include?(viewing_direction)
86
+ end
85
87
 
86
- opts.on('--access LEVEL', 'The access level for this object. ' \
87
- 'Either "world", "stanford", "location-based", "citation-only" or "dark"') do |level|
88
- options[:access] = level if %w[world stanford location-based citation-only dark].include?(level)
89
- end
88
+ opts.on('--access LEVEL', 'The access level for this object. ' \
89
+ 'Either "world", "stanford", "location-based", "citation-only" or "dark"') do |level|
90
+ options[:access] = level if %w[world stanford location-based citation-only dark].include?(level)
91
+ end
90
92
 
91
- opts.on('--files-metadata FILES_METADATA', 'A JSON object representing per-file metadata') do |files_metadata|
92
- options[:files_metadata] = JSON.parse(files_metadata)
93
- end
93
+ opts.on('--files-metadata FILES_METADATA', 'A JSON object representing per-file metadata') do |files_metadata|
94
+ options[:files_metadata] = JSON.parse(files_metadata)
95
+ end
94
96
 
95
- opts.on('--strategy STRATEGY',
96
- 'The strategy to use for distributing files into filesets. Either "default" or "filename"') do |strategy|
97
- strategy_class = case strategy
98
- when 'filename'
99
- SdrClient::Deposit::MatchingFileGroupingStrategy
100
- when 'default'
101
- SdrClient::Deposit::SingleFileGroupingStrategy
102
- else
103
- warn "Unknown strategy #{strategy}"
104
- exit(1)
105
- end
106
- options[:grouping_strategy] = strategy_class
107
- end
97
+ opts.on('--strategy STRATEGY',
98
+ 'The strategy to use for distributing files into filesets. Either "default" or "filename"') do |strategy|
99
+ strategy_class = case strategy
100
+ when 'filename'
101
+ SdrClient::Deposit::MatchingFileGroupingStrategy
102
+ when 'default'
103
+ SdrClient::Deposit::SingleFileGroupingStrategy
104
+ else
105
+ warn "Unknown strategy #{strategy}"
106
+ exit(1)
107
+ end
108
+ options[:grouping_strategy] = strategy_class
109
+ end
108
110
 
109
- opts.on('-h', '--help', 'Display this screen') do
110
- puts opts
111
- exit
112
- end
113
- end,
111
+ opts.on('-h', '--help', 'Display this screen') do
112
+ puts opts
113
+ exit
114
+ end
115
+ end
116
+
117
+ subcommands = {
118
+ 'deposit' => deposit_options,
119
+ 'register' => deposit_options,
114
120
  'login' => OptionParser.new
115
121
  }
116
122
 
@@ -4,6 +4,8 @@ require 'dry/monads'
4
4
  require 'faraday'
5
5
  require 'active_support'
6
6
  require 'active_support/core_ext/object/json'
7
+ require 'active_support/core_ext/hash/indifferent_access'
8
+ require 'cocina/models'
7
9
 
8
10
  require 'sdr_client/version'
9
11
  require 'sdr_client/deposit'
@@ -12,6 +14,7 @@ require 'sdr_client/credentials'
12
14
  require 'sdr_client/login'
13
15
  require 'sdr_client/login_prompt'
14
16
  require 'sdr_client/cli'
17
+ require 'sdr_client/connection'
15
18
 
16
19
  module SdrClient
17
20
  class Error < StandardError; end
@@ -6,7 +6,9 @@ module SdrClient
6
6
  def self.start(command, options)
7
7
  case command
8
8
  when 'deposit'
9
- SdrClient::Deposit.run(options)
9
+ SdrClient::Deposit.run(accession: true, **options)
10
+ when 'register'
11
+ SdrClient::Deposit.run(accession: false, **options)
10
12
  when 'login'
11
13
  status = SdrClient::Login.run(options)
12
14
  puts status.value if status.failure?
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrClient
4
+ # The connection to the server
5
+ class Connection
6
+ include Dry::Monads[:result]
7
+
8
+ def initialize(url:, token: Credentials.read)
9
+ @url = url
10
+ @token = token
11
+ end
12
+
13
+ def connection
14
+ @connection ||= Faraday.new(url: url) do |conn|
15
+ conn.authorization :Bearer, token
16
+ conn.adapter :net_http
17
+ end
18
+ end
19
+
20
+ # This is only available to certain blessed accounts (argo) as it gives the
21
+ # token that allows you to act as any other user. Thus the caller must authenticate
22
+ # the user (e.g. using Shibboleth) before calling this method with their email address.
23
+ # @param [String] the email address of the person to proxy to.
24
+ # @return [Result] the token for the account
25
+ def proxy(to)
26
+ response = connection.post("/v1/auth/proxy?to=#{to}")
27
+ case response.status
28
+ when 200
29
+ Success(response.body)
30
+ else
31
+ Failure("Status: #{response.status}\n#{response.body}")
32
+ end
33
+ end
34
+
35
+ delegate :put, :post, to: :connection
36
+
37
+ private
38
+
39
+ attr_reader :url, :token
40
+ end
41
+ end
@@ -23,10 +23,9 @@ module SdrClient
23
23
  url:,
24
24
  files: [],
25
25
  files_metadata: {},
26
+ accession: false,
26
27
  grouping_strategy: SingleFileGroupingStrategy,
27
28
  logger: Logger.new(STDOUT))
28
- token = Credentials.read
29
-
30
29
  augmented_metadata = FileMetadataBuilder.build(files: files, files_metadata: files_metadata)
31
30
  metadata = Request.new(label: label,
32
31
  type: type,
@@ -41,8 +40,11 @@ module SdrClient
41
40
  embargo_access: embargo_access,
42
41
  viewing_direction: viewing_direction,
43
42
  files_metadata: augmented_metadata)
44
- Process.new(metadata: metadata, url: url, token: token, files: files,
45
- grouping_strategy: grouping_strategy, logger: logger).run
43
+ connection = Connection.new(url: url)
44
+ Process.new(metadata: metadata, connection: connection, files: files,
45
+ grouping_strategy: grouping_strategy,
46
+ accession: accession,
47
+ logger: logger).run
46
48
  end
47
49
  # rubocop:enable Metrics/MethodLength
48
50
  # rubocop:enable Metrics/ParameterLists
@@ -34,9 +34,10 @@ module SdrClient
34
34
  administrative: {
35
35
  sdrPreserve: @preserve,
36
36
  shelve: @shelve
37
- }
37
+ },
38
+ version: 1,
39
+ hasMessageDigests: message_digests
38
40
  }.tap do |json|
39
- json['hasMessageDigests'] = message_digests unless message_digests.empty?
40
41
  json['hasMimeType'] = @mime_type if @mime_type
41
42
  json['use'] = @use if @use
42
43
  end
@@ -25,7 +25,8 @@ module SdrClient
25
25
  "label": label,
26
26
  structural: {
27
27
  contains: files.map(&:as_json)
28
- }
28
+ },
29
+ version: 1
29
30
  }
30
31
  end
31
32
 
@@ -8,15 +8,12 @@ module SdrClient
8
8
  class ModelProcess
9
9
  DRO_PATH = '/v1/resources'
10
10
  # @param [Cocina::Model::RequestDRO] request_dro for depositing
11
- # @param [String] url the server to send to
12
- # @param [String] token the bearer auth token for the server
11
+ # @param [Connection] connection the connection to use
13
12
  # @param [Array<String>] files a list of file names to upload
14
13
  # @param [Logger] logger the logger to use
15
- def initialize(request_dro:, url:,
16
- token:, files: [], logger: Logger.new(STDOUT))
14
+ def initialize(request_dro:, connection:, files: [], logger: Logger.new(STDOUT))
17
15
  @files = files
18
- @url = url
19
- @token = token
16
+ @connection = connection
20
17
  @request_dro = request_dro
21
18
  @logger = logger
22
19
  end
@@ -35,7 +32,7 @@ module SdrClient
35
32
 
36
33
  private
37
34
 
38
- attr_reader :request_dro, :files, :url, :token, :logger
35
+ attr_reader :request_dro, :files, :logger, :connection
39
36
 
40
37
  def check_files_exist
41
38
  logger.info('checking to see if files exist')
@@ -77,13 +74,6 @@ module SdrClient
77
74
  raise "unexpected response: #{response.status} #{response.body}"
78
75
  end
79
76
 
80
- def connection
81
- @connection ||= Faraday.new(url: url) do |conn|
82
- conn.authorization :Bearer, token
83
- conn.adapter :net_http
84
- end
85
- end
86
-
87
77
  # Map of filenames to mimetypes
88
78
  def mime_types
89
79
  @mime_types ||=
@@ -9,22 +9,24 @@ module SdrClient
9
9
  DRO_PATH = '/v1/resources'
10
10
  # @param [Request] metadata information about the object
11
11
  # @param [Class] grouping_strategy class whose run method groups an array of uploads
12
- # @param [String] url the server to send to
13
- # @param [String] token the bearer auth token for the server
12
+ # @param [String] connection the server connection to use
14
13
  # @param [Array<String>] files a list of file names to upload
14
+ # @param [Boolean] accession should the accessionWF be started
15
15
  # @param [Logger] logger the logger to use
16
+ #
16
17
  # rubocop:disable Metrics/ParameterLists
17
- def initialize(metadata:, grouping_strategy: SingleFileGroupingStrategy, url:,
18
- token:, files: [], logger: Logger.new(STDOUT))
18
+ def initialize(metadata:, grouping_strategy: SingleFileGroupingStrategy,
19
+ connection:, files: [], accession:, logger: Logger.new(STDOUT))
19
20
  @files = files
20
- @url = url
21
- @token = token
21
+ @connection = connection
22
22
  @metadata = metadata
23
23
  @logger = logger
24
24
  @grouping_strategy = grouping_strategy
25
+ @accession = accession
25
26
  end
26
27
  # rubocop:enable Metrics/ParameterLists
27
28
 
29
+ # rubocop:disable Metrics/AbcSize
28
30
  def run
29
31
  check_files_exist
30
32
  upload_responses = UploadFiles.new(files: files,
@@ -35,12 +37,14 @@ module SdrClient
35
37
  grouping_strategy: grouping_strategy,
36
38
  logger: logger)
37
39
  request = metadata_builder.with_uploads(upload_responses)
38
- upload_metadata(request.as_json)
40
+ model = Cocina::Models.build_request(request.as_json.with_indifferent_access)
41
+ upload_metadata(model.to_h)
39
42
  end
43
+ # rubocop:enable Metrics/AbcSize
40
44
 
41
45
  private
42
46
 
43
- attr_reader :metadata, :files, :url, :token, :logger, :grouping_strategy
47
+ attr_reader :metadata, :files, :connection, :logger, :grouping_strategy
44
48
 
45
49
  def check_files_exist
46
50
  logger.info('checking to see if files exist')
@@ -49,11 +53,15 @@ module SdrClient
49
53
  end
50
54
  end
51
55
 
56
+ def accession?
57
+ @accession
58
+ end
59
+
60
+ # @param [Hash<Symbol,String>] the result of the metadata call
61
+ # @param [Boolean] accession should the accessionWF be started
52
62
  # @return [Hash<Symbol,String>] the result of the metadata call
53
63
  def upload_metadata(metadata)
54
- logger.info("Starting upload metadata: #{metadata}")
55
- request_json = JSON.generate(metadata)
56
- response = connection.post(DRO_PATH, request_json, 'Content-Type' => 'application/json')
64
+ response = metadata_request(metadata)
57
65
  unexpected_response(response) unless response.status == 201
58
66
 
59
67
  logger.info("Response from server: #{response.body}")
@@ -61,6 +69,18 @@ module SdrClient
61
69
  { druid: JSON.parse(response.body)['druid'], background_job: response.headers['Location'] }
62
70
  end
63
71
 
72
+ def metadata_request(metadata)
73
+ logger.debug("Starting upload metadata: #{metadata}")
74
+
75
+ connection.post(path, JSON.generate(metadata), 'Content-Type' => 'application/json')
76
+ end
77
+
78
+ def path
79
+ path = DRO_PATH
80
+ path += '?accession=true' if accession?
81
+ path
82
+ end
83
+
64
84
  def unexpected_response(response)
65
85
  raise "There was an error with your request: #{response.body}" if response.status == 400
66
86
  raise 'There was an error with your credentials. Perhaps they have expired?' if response.status == 401
@@ -68,13 +88,6 @@ module SdrClient
68
88
  raise "unexpected response: #{response.status} #{response.body}"
69
89
  end
70
90
 
71
- def connection
72
- @connection ||= Faraday.new(url: url) do |conn|
73
- conn.authorization :Bearer, token
74
- conn.adapter :net_http
75
- end
76
- end
77
-
78
91
  def mime_types
79
92
  @mime_types ||=
80
93
  Hash[
@@ -49,10 +49,10 @@ module SdrClient
49
49
  type: type,
50
50
  administrative: administrative,
51
51
  identification: identification,
52
- structural: structural
53
- }.tap do |json|
54
- json[:label] = label if label
55
- end
52
+ structural: structural,
53
+ version: 1,
54
+ label: label.nil? ? ':auto' : label
55
+ }
56
56
  end
57
57
 
58
58
  # @return [Request] a clone of this request with the file_sets added
@@ -9,7 +9,7 @@ module SdrClient
9
9
  BLOB_PATH = '/v1/direct_uploads'
10
10
  # @param [Array<String>] files a list of filepaths to upload
11
11
  # @param [Logger] logger the logger to use
12
- # @param [Faraday::Connection] connection
12
+ # @param [Connection] connection
13
13
  # @param [Hash<String,String] mime_types a map of filenames to mime types
14
14
  def initialize(files:, mime_types:, logger:, connection:)
15
15
  @files = files
@@ -7,12 +7,12 @@ module SdrClient
7
7
  extend Dry::Monads[:result]
8
8
 
9
9
  # @return [Result] the status of the call
10
- def self.run(url:, login_service: LoginPrompt)
10
+ def self.run(url:, login_service: LoginPrompt, credential_store: Credentials)
11
11
  request_json = JSON.generate(login_service.run)
12
12
  response = Faraday.post(url + LOGIN_PATH, request_json, 'Content-Type' => 'application/json')
13
13
  case response.status
14
14
  when 200
15
- Credentials.write(response.body)
15
+ credential_store.write(response.body)
16
16
  Success()
17
17
  when 400
18
18
  Failure('Email address is not a valid email')
@@ -9,9 +9,8 @@ module SdrClient
9
9
  files: [],
10
10
  url:,
11
11
  logger: Logger.new(STDOUT))
12
- token = Credentials.read
13
-
14
- ModelProcess.new(request_dro: request_dro, url: url, token: token, files: files, logger: logger).run
12
+ connection = Connection.new(url: url)
13
+ ModelProcess.new(request_dro: request_dro, connection: connection, files: files, logger: logger).run
15
14
  end
16
15
  end
17
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SdrClient
4
- VERSION = '0.17.2'
4
+ VERSION = '0.22.0'
5
5
  end
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ['lib']
29
29
 
30
30
  spec.add_dependency 'activesupport'
31
- spec.add_dependency 'cocina-models', '~> 0.28.0'
31
+ spec.add_dependency 'cocina-models', '~> 0.31.0'
32
32
  spec.add_dependency 'dry-monads'
33
33
  spec.add_dependency 'faraday', '>= 0.16'
34
34
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdr-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.2
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-11 00:00:00.000000000 Z
11
+ date: 2020-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.28.0
33
+ version: 0.31.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.28.0
40
+ version: 0.31.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: dry-monads
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -189,6 +189,7 @@ files:
189
189
  - lib/sdr-client.rb
190
190
  - lib/sdr_client.rb
191
191
  - lib/sdr_client/cli.rb
192
+ - lib/sdr_client/connection.rb
192
193
  - lib/sdr_client/credentials.rb
193
194
  - lib/sdr_client/deposit.rb
194
195
  - lib/sdr_client/deposit/file.rb
@@ -232,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
233
  - !ruby/object:Gem::Version
233
234
  version: '0'
234
235
  requirements: []
235
- rubygems_version: 3.0.3
236
+ rubygems_version: 3.1.2
236
237
  signing_key:
237
238
  specification_version: 4
238
239
  summary: The CLI for https://github.com/sul-dlss/sdr-api