sdr-client 0.17.2 → 0.22.0

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