sdr-client 0.1.0 → 0.2.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: 97ae47ce6b50c4fd12228990d25dc3fb63432ba1a1e0fff9a1e614dece546c69
4
- data.tar.gz: f64a044b2373cda941ec7fe593d2eacc1602a6178963cf80c0637415ecded9f4
3
+ metadata.gz: 3a995f725226eef2f793d8b0b45e487c37a6e34899b223a1f2c6864f32204325
4
+ data.tar.gz: dd947253f76fe8dcfd20565537e89fa86c4b6b13a4e6c68b2ecf285d30406c08
5
5
  SHA512:
6
- metadata.gz: c573233fcff72bb1c1402d170d048b03182ac7dd45c81a7531dd02529869f2354c9d73bd8522cbb1ba463c52bb933cf692a67e3b7932c77f3746bf2276253913
7
- data.tar.gz: '0848314bb1fd0b02ca2add9e09c92185fbcf8d9db642d683fcb116dae2cbd7ca6df97d5e9e3464f4b2ab71fa1027eda3d67d07723b50bf3ffc6f08274590f649'
6
+ metadata.gz: f3f0c8d2c9994c4a0408438199105dfdcccb0901e4905220cbf99fd69b3d5e9a735752c3de638264c1397986985ecb2a9f992ab1f1fad2e3245025e78eed1dd0
7
+ data.tar.gz: 300591272660ad09e74bc992742c3c26c14251bd107252b441a2e2856ed981749dfd5ff298704b13c360a0b3dab0cc89fceb2282a9f301fe24909eaac0b8de0d
@@ -1,22 +1,21 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2020-01-07 23:33:21 -0800 using RuboCop version 0.79.0.
3
+ # on 2020-01-10 17:16:01 -0600 using RuboCop version 0.79.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
9
  # Offense count: 1
10
- # Cop supports --auto-correct.
11
- # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
12
- Layout/ExtraSpacing:
13
- Exclude:
14
- - 'sdr-client.gemspec'
10
+ Metrics/AbcSize:
11
+ Max: 16
15
12
 
16
13
  # Offense count: 1
17
- # Cop supports --auto-correct.
18
- # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator.
19
- # SupportedStylesForExponentOperator: space, no_space
20
- Layout/SpaceAroundOperators:
21
- Exclude:
22
- - 'sdr-client.gemspec'
14
+ # Configuration parameters: CountComments, ExcludedMethods.
15
+ Metrics/MethodLength:
16
+ Max: 13
17
+
18
+ # Offense count: 2
19
+ # Configuration parameters: CountKeywordArgs.
20
+ Metrics/ParameterLists:
21
+ Max: 8
@@ -13,5 +13,8 @@ before_script:
13
13
  after_script:
14
14
  - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
15
15
 
16
+ env:
17
+ global:
18
+ - CC_TEST_REPORTER_ID=859fcfe88b00c026d15dce30e838e2299face8088b49fe62bc3a02d1507ce3d5
16
19
  notifications:
17
20
  email: false
data/README.md CHANGED
@@ -10,7 +10,16 @@ The code for the SDR API server is at https://github.com/sul-dlss/sdr-api
10
10
 
11
11
  ## Usage
12
12
 
13
+ Log in:
14
+ ```
15
+ sdr --service-url http://sdr-api-server:3000 login
16
+ ```
17
+
18
+
13
19
  Deposit a new object:
14
20
  ```
15
- sdr --service-url http://sdr-api-server:3000 deposit --label 'hey there' file1.png file2.png
21
+ sdr --service-url https://sdr-api-server:3000 deposit --label 'hey there' \
22
+ --admin-policy 'druid:bk123gh4567' \
23
+ --collection 'druid:gh456kw9876' \
24
+ --source-id 'googlebooks:stanford_12345' file1.png file2.png
16
25
  ```
data/exe/sdr CHANGED
@@ -10,7 +10,28 @@ global = OptionParser.new do |opts|
10
10
  options[:url] = url
11
11
  end
12
12
  opts.on('-h', '--help', 'Display this screen') do
13
- puts opts
13
+ puts <<~HELP
14
+ DESCRIPTION:
15
+ The SDR Command Line Interface is a tool to interact with the Stanford Digital Repository.
16
+
17
+ SYNOPSIS:
18
+ sdr [options] <command>
19
+
20
+ OPTIONS:
21
+ --service-url (string)
22
+ Override the command's default URL with the given URL.
23
+
24
+ -h, --help
25
+ Displays this screen
26
+
27
+ COMMANDS
28
+ deposit
29
+ deposit files to the SDR
30
+
31
+ login
32
+ Will prompt for email & password and exchange it for an login token, which it saves in ~/.sdr/token
33
+
34
+ HELP
14
35
  exit
15
36
  end
16
37
  end
@@ -23,11 +44,28 @@ subcommands = {
23
44
  opts.on('--label LABEL', 'The object label') do |label|
24
45
  options[:label] = label
25
46
  end
47
+
48
+ opts.on('--admin-policy ADMIN_POLICY', 'The druid identifier of the admin policy object') do |apo|
49
+ options[:apo] = apo
50
+ end
51
+ opts.on('--collection COLLECTION', 'The druid identifier of the collection object') do |collection|
52
+ options[:collection] = collection
53
+ end
54
+
55
+ opts.on('--catkey CATKEY', 'The catkey for this item') do |catkey|
56
+ options[:catkey] = catkey
57
+ end
58
+
59
+ opts.on('--source-id SOURCE_ID', 'The source id for this object') do |source_id|
60
+ options[:source_id] = source_id
61
+ end
62
+
26
63
  opts.on('-h', '--help', 'Display this screen') do
27
64
  puts opts
28
65
  exit
29
66
  end
30
- end
67
+ end,
68
+ 'login' => OptionParser.new
31
69
  }
32
70
 
33
71
  unless subcommands.key?(command)
@@ -2,6 +2,9 @@
2
2
 
3
3
  require 'sdr_client/version'
4
4
  require 'sdr_client/deposit'
5
+ require 'sdr_client/credentials'
6
+ require 'sdr_client/login'
7
+ require 'sdr_client/login_prompt'
5
8
  require 'sdr_client/cli'
6
9
  require 'faraday'
7
10
 
@@ -7,6 +7,10 @@ module SdrClient
7
7
  case command
8
8
  when 'deposit'
9
9
  SdrClient::Deposit.run(options)
10
+ when 'login'
11
+ SdrClient::Login.run(options)
12
+ else
13
+ raise "Unknown command #{command}"
10
14
  end
11
15
  end
12
16
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrClient
4
+ # The stored credentials
5
+ class Credentials
6
+ # @param [String] a json string that contains a field 'token'
7
+ def self.write(body)
8
+ json = JSON.parse(body)
9
+ Dir.mkdir(credentials_path, 0o700) unless Dir.exist?(credentials_path)
10
+ File.open(credentials_file, 'w', 0o600) do |file|
11
+ file.write(json.fetch('token'))
12
+ end
13
+ puts 'Signed in.'
14
+ end
15
+
16
+ def self.read
17
+ return IO.readlines(credentials_file, chomp: true).first if File.exist?(credentials_file)
18
+
19
+ puts 'Log in first'
20
+ exit(1)
21
+ end
22
+
23
+ def self.credentials_path
24
+ @credentials_path ||= File.join(Dir.home, '.sdr')
25
+ end
26
+
27
+ def self.credentials_file
28
+ File.join(credentials_path, 'credentials')
29
+ end
30
+ end
31
+ end
@@ -3,10 +3,22 @@
3
3
  module SdrClient
4
4
  # The namespace for the "deposit" command
5
5
  module Deposit
6
- def self.run(label:,
6
+ def self.run(label: nil,
7
7
  type: 'http://cocina.sul.stanford.edu/models/book.jsonld',
8
+ apo:,
9
+ collection:,
10
+ catkey: nil,
11
+ source_id:,
8
12
  url:, files: [])
9
- Process.new(label: label, type: type, url: url, files: files).run
13
+ token = Credentials.read
14
+
15
+ metadata = Request.new(label: label,
16
+ type: type,
17
+ apo: apo,
18
+ collection: collection,
19
+ source_id: source_id,
20
+ catkey: catkey)
21
+ Process.new(metadata: metadata, url: url, token: token, files: files).run
10
22
  end
11
23
  end
12
24
  end
@@ -6,13 +6,18 @@ module SdrClient
6
6
  module Deposit
7
7
  # The process for doing a deposit
8
8
  class Process
9
- BLOB_PATH = '/rails/active_storage/direct_uploads'
9
+ BLOB_PATH = '/v1/direct_uploads'
10
10
  DRO_PATH = '/v1/resources'
11
- def initialize(label:, type:, url:, files: [], logger: Logger.new(STDOUT))
12
- @label = label
13
- @type = type
11
+ # @param [Request] metadata information about the object
12
+ # @param [String] url the server to send to
13
+ # @param [String] token the bearer auth token for the server
14
+ # @param [Array<String>] files a list of file names to upload
15
+ # @param [Logger] logger the logger to use
16
+ def initialize(metadata:, url:, token:, files: [], logger: Logger.new(STDOUT))
14
17
  @files = files
15
18
  @url = url
19
+ @token = token
20
+ @metadata = metadata
16
21
  @logger = logger
17
22
  end
18
23
 
@@ -21,15 +26,13 @@ module SdrClient
21
26
  file_metadata = collect_file_metadata
22
27
  upload_responses = upload_file_metadata(file_metadata)
23
28
  upload_files(upload_responses)
24
- metadata = Request.new(label: label,
25
- type: type,
26
- uploads: upload_responses.values)
27
- upload_metadata(metadata.as_json)
29
+ request = metadata.with_uploads(upload_responses.values)
30
+ upload_metadata(request.as_json)
28
31
  end
29
32
 
30
33
  private
31
34
 
32
- attr_reader :label, :type, :files, :url, :logger
35
+ attr_reader :metadata, :files, :url, :token, :logger
33
36
 
34
37
  def check_files_exist
35
38
  logger.info('checking to see if files exist')
@@ -51,7 +54,7 @@ module SdrClient
51
54
 
52
55
  def direct_upload(metadata_json)
53
56
  logger.info("Starting an upload request: #{metadata_json}")
54
- response = Faraday.post(url + BLOB_PATH, metadata_json, 'Content-Type' => 'application/json')
57
+ response = connection.post(BLOB_PATH, metadata_json, 'Content-Type' => 'application/json')
55
58
  raise "unexpected response: #{response.inspect}" unless response.status == 200
56
59
 
57
60
  logger.info("Response from server: #{response.body}")
@@ -73,10 +76,8 @@ module SdrClient
73
76
 
74
77
  def upload_file(filename:, url:, content_type:, content_length:)
75
78
  logger.info("Uploading `#{filename}' to #{url}")
76
- conn = Faraday.new(url) do |builder|
77
- builder.adapter :net_http
78
- end
79
- upload_response = conn.put(url) do |req|
79
+
80
+ upload_response = connection.put(url) do |req|
80
81
  req.body = File.open(filename)
81
82
  req.headers['Content-Type'] = content_type
82
83
  req.headers['Content-Length'] = content_length.to_s
@@ -85,15 +86,30 @@ module SdrClient
85
86
  raise "unexpected response: #{upload_response.inspect}" unless upload_response.status == 204
86
87
  end
87
88
 
89
+ # @return [Hash<Symbol,String>] the result of the metadata call
88
90
  def upload_metadata(metadata)
89
91
  logger.info("Starting upload metadata: #{metadata}")
90
92
  request_json = JSON.generate(metadata)
91
- response = Faraday.post(url + DRO_PATH, request_json, 'Content-Type' => 'application/json')
92
- raise "unexpected response: #{response.inspect}" unless response.status == 200
93
+ response = connection.post(DRO_PATH, request_json, 'Content-Type' => 'application/json')
94
+ unexpected_response(response) unless response.status == 201
93
95
 
94
96
  logger.info("Response from server: #{response.body}")
95
97
 
96
- JSON.parse(response.body)
98
+ { druid: JSON.parse(response.body)['druid'], background_job: response.headers['Location'] }
99
+ end
100
+
101
+ def unexpected_response(response)
102
+ raise "unexpected response: #{response.inspect}" unless response.status == 400
103
+
104
+ puts "\nThere was an error with your request: #{response.body}"
105
+ exit(1)
106
+ end
107
+
108
+ def connection
109
+ @connection ||= Faraday.new(url: url) do |conn|
110
+ conn.authorization :Bearer, token
111
+ conn.adapter :net_http
112
+ end
97
113
  end
98
114
  end
99
115
  end
@@ -4,33 +4,70 @@ module SdrClient
4
4
  module Deposit
5
5
  # This represents the metadata that we send to the server for doing a deposit
6
6
  class Request
7
- CONTEXT = 'http://cocina.sul.stanford.edu/contexts/cocina-base.jsonld'
8
-
9
7
  # @param [String] label the required object label
10
8
  # @param [String] type (http://cocina.sul.stanford.edu/models/object.jsonld) the required object type.
11
9
  # @param [Array<SdrClient::Deposit::Files::DirectUploadResponse>] uploads the uploaded files to attach.
12
- def initialize(label:,
10
+ def initialize(label: nil,
11
+ apo:,
12
+ collection:,
13
+ source_id:,
14
+ catkey: nil,
13
15
  type: 'http://cocina.sul.stanford.edu/models/object.jsonld',
14
16
  uploads: [])
15
17
  @label = label
16
18
  @type = type
19
+ @source_id = source_id
20
+ @collection = collection
21
+ @catkey = catkey
22
+ @apo = apo
17
23
  @uploads = uploads
18
24
  end
19
25
 
20
26
  def as_json
21
27
  {
22
- "@context": CONTEXT,
23
- "@type": type,
24
- label: label,
25
- structural: {
26
- hasMember: file_sets_as_json
27
- }
28
- }
28
+ access: {},
29
+ type: type,
30
+ administrative: administrative,
31
+ identification: identification,
32
+ structural: structural
33
+ }.tap do |json|
34
+ json[:label] = label if label
35
+ end
36
+ end
37
+
38
+ # @return [Request] a clone of this request with the uploads added
39
+ def with_uploads(uploads)
40
+ Request.new(label: label,
41
+ apo: apo,
42
+ collection: collection,
43
+ source_id: source_id,
44
+ catkey: catkey,
45
+ type: type,
46
+ uploads: uploads)
29
47
  end
30
48
 
31
49
  private
32
50
 
33
- attr_reader :label, :uploads, :type
51
+ attr_reader :label, :uploads, :source_id, :catkey, :apo, :collection, :type
52
+
53
+ def administrative
54
+ {
55
+ hasAdminPolicy: apo
56
+ }
57
+ end
58
+
59
+ def identification
60
+ { sourceId: source_id }.tap do |json|
61
+ json[:catkey] = catkey if catkey
62
+ end
63
+ end
64
+
65
+ def structural
66
+ {
67
+ isMemberOf: collection,
68
+ hasMember: file_sets_as_json
69
+ }
70
+ end
34
71
 
35
72
  # In this case there is a 1-1 mapping between Files and FileSets,
36
73
  # but this doesn't always have to be the case. We could change this in the
@@ -38,8 +75,7 @@ module SdrClient
38
75
  def file_sets_as_json
39
76
  uploads.map do |upload|
40
77
  {
41
- "@context": CONTEXT,
42
- "@type": 'http://cocina.sul.stanford.edu/models/fileset.jsonld',
78
+ "type": 'http://cocina.sul.stanford.edu/models/fileset.jsonld',
43
79
  label: upload.filename,
44
80
  structural: {
45
81
  hasMember: [upload.signed_id]
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SdrClient
4
+ # The namespace for the "login" command
5
+ module Login
6
+ LOGIN_PATH = '/v1/auth/login'
7
+ def self.run(url:, login_service: LoginPrompt)
8
+ request_json = JSON.generate(login_service.run)
9
+ response = Faraday.post(url + LOGIN_PATH, request_json, 'Content-Type' => 'application/json')
10
+ case response.status
11
+ when 200
12
+ Credentials.write(response.body)
13
+ when 400
14
+ puts 'Email address is not a valid email'
15
+ when 401
16
+ puts 'Invalid username or password'
17
+ else
18
+ puts "Status: #{response.status}"
19
+ puts response.body
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+
5
+ module SdrClient
6
+ # The namespace for the "login" command
7
+ module LoginPrompt
8
+ def self.run
9
+ print 'Email: '
10
+ email = gets
11
+ email.strip!
12
+ print 'Password: '
13
+ password = $stdin.noecho(&:gets)
14
+ password.strip!
15
+ puts
16
+ { email: email, password: password }
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SdrClient
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
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.1.0
4
+ version: 0.2.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-01-09 00:00:00.000000000 Z
11
+ date: 2020-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -146,11 +146,14 @@ files:
146
146
  - exe/sdr
147
147
  - lib/sdr_client.rb
148
148
  - lib/sdr_client/cli.rb
149
+ - lib/sdr_client/credentials.rb
149
150
  - lib/sdr_client/deposit.rb
150
151
  - lib/sdr_client/deposit/files/direct_upload_request.rb
151
152
  - lib/sdr_client/deposit/files/direct_upload_response.rb
152
153
  - lib/sdr_client/deposit/process.rb
153
154
  - lib/sdr_client/deposit/request.rb
155
+ - lib/sdr_client/login.rb
156
+ - lib/sdr_client/login_prompt.rb
154
157
  - lib/sdr_client/version.rb
155
158
  - sdr-client.gemspec
156
159
  homepage: https://github.com/sul-dlss/sdr-client
@@ -174,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
177
  - !ruby/object:Gem::Version
175
178
  version: '0'
176
179
  requirements: []
177
- rubygems_version: 3.0.6
180
+ rubygems_version: 3.0.3
178
181
  signing_key:
179
182
  specification_version: 4
180
183
  summary: The CLI for https://github.com/sul-dlss/sdr-api