sdr-client 0.71.0 → 0.72.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: 4e12d569ac9f642a42311a80b9594a64398742274be65bd2298ddea5806aaa62
4
- data.tar.gz: ba7c87706cf15a5904a44ac58f2d4303023eda9cef43224ad74e4b3e4ab4957b
3
+ metadata.gz: 899fec033d20f23ab887672fcfe7c706f9c6e5b02b7fd6975db5560be96b788c
4
+ data.tar.gz: cb68aa02ff81a59f50bdbc5003407765caa25d3e73d64f663e40c3990d1509df
5
5
  SHA512:
6
- metadata.gz: 3f64850d5858f86b66a3cc1db3d38723ed35c78c4010f59e92af3affe945cc5bdc43dd13ff473f0e44ba256abb3a6ad1b7fac7bff8c76edca908271b64037bb7
7
- data.tar.gz: 6b31ab8f77403f370e0ce7d520f98ec989e670bcb9700c779e039c3a19f84022411fb03e3d6d4ed057ee414f58229526722ae8b219d2285359bc2e99c0ef2d14
6
+ metadata.gz: a53a001759ad7c941c1713999f1dfcd15a43c4200fff5644605dd277f2e8ce4c52497e8e1662a7b43fb896860f20dfc42380a8249fa47702e9106a5ecf4bf1c1
7
+ data.tar.gz: f1de6ce23106680288c2994e7ba085a9ef4aed3cbf2ecbbb315c8c4601a01aba134d5bcfd3b316fb7a242a99ebcad89cbf8f22929e2099cad3f001f62ac3c74a
data/.rubocop.yml CHANGED
@@ -19,7 +19,9 @@ Metrics/BlockLength:
19
19
 
20
20
  Metrics/ClassLength:
21
21
  Exclude:
22
+ - 'lib/sdr_client/cli.rb'
22
23
  - 'lib/sdr_client/deposit/request.rb'
24
+ - 'lib/sdr_client/update.rb'
23
25
 
24
26
  Naming/FileName:
25
27
  Exclude:
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
- # on 2022-03-08 23:08:23 UTC using RuboCop version 1.25.1.
3
+ # on 2022-03-11 00:50:54 UTC using RuboCop version 1.25.1.
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
@@ -26,17 +26,19 @@ Lint/UnusedMethodArgument:
26
26
  - 'lib/sdr_client/deposit/file_type_file_set_strategy.rb'
27
27
  - 'lib/sdr_client/deposit/image_file_set_strategy.rb'
28
28
 
29
- # Offense count: 1
29
+ # Offense count: 2
30
30
  # Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max.
31
31
  Metrics/AbcSize:
32
32
  Exclude:
33
33
  - 'lib/sdr_client/cli.rb'
34
+ - 'lib/sdr_client/update.rb'
34
35
 
35
- # Offense count: 1
36
+ # Offense count: 2
36
37
  # Configuration parameters: IgnoredMethods, Max.
37
38
  Metrics/CyclomaticComplexity:
38
39
  Exclude:
39
40
  - 'lib/sdr_client/cli.rb'
41
+ - 'lib/sdr_client/update.rb'
40
42
 
41
43
  # Offense count: 11
42
44
  # Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
@@ -50,12 +52,13 @@ Metrics/MethodLength:
50
52
  - 'lib/sdr_client/login.rb'
51
53
  - 'lib/sdr_client/update.rb'
52
54
 
53
- # Offense count: 8
55
+ # Offense count: 10
54
56
  # Configuration parameters: Max, CountAsOne.
55
57
  RSpec/ExampleLength:
56
58
  Exclude:
57
59
  - 'spec/sdr_client/deposit_spec.rb'
58
60
  - 'spec/sdr_client/model_deposit_spec.rb'
61
+ - 'spec/sdr_client/update_spec.rb'
59
62
 
60
63
  # Offense count: 3
61
64
  # Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly.
@@ -119,6 +122,12 @@ Style/KeywordParametersOrder:
119
122
  - 'lib/sdr_client/deposit.rb'
120
123
  - 'lib/sdr_client/deposit/request.rb'
121
124
 
125
+ # Offense count: 1
126
+ # Cop supports --auto-correct.
127
+ Style/MultilineTernaryOperator:
128
+ Exclude:
129
+ - 'lib/sdr_client/update.rb'
130
+
122
131
  # Offense count: 2
123
132
  # Cop supports --auto-correct-all.
124
133
  # Configuration parameters: Mode.
@@ -127,12 +136,14 @@ Style/StringConcatenation:
127
136
  - 'lib/sdr_client/deposit/create_resource.rb'
128
137
  - 'spec/sdr_client/deposit_spec.rb'
129
138
 
130
- # Offense count: 16
139
+ # Offense count: 19
131
140
  # Cop supports --auto-correct.
132
141
  # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
133
142
  # URISchemes: http, https
134
143
  Layout/LineLength:
135
144
  Exclude:
145
+ - 'lib/sdr_client/cli.rb'
136
146
  - 'lib/sdr_client/deposit/model_process.rb'
147
+ - 'lib/sdr_client/update.rb'
137
148
  - 'spec/sdr_client/deposit/model_process_spec.rb'
138
149
  - 'spec/sdr_client/deposit/process_spec.rb'
data/README.md CHANGED
@@ -17,55 +17,82 @@ We recommend using the latest 3.x release of Ruby.
17
17
 
18
18
  ## Usage
19
19
 
20
+ Get general help (e.g., list out commands):
21
+ ```
22
+ sdr help
23
+ # or:
24
+ sdr -h
25
+ # or:
26
+ sdr --help
27
+ ```
28
+
29
+ Get help for a specific command:
30
+ ```
31
+ sdr help register
32
+ ```
33
+
20
34
  Log in:
21
35
  ```
22
- sdr --service-url https://sdr-api-server:3000 login
36
+ sdr login --url https://sdr-api-server:3000
37
+ ```
38
+
39
+ Display version of sdr-client:
40
+ ```
41
+ sdr version
23
42
  ```
24
43
 
25
44
  Register a new object:
26
45
  ```
27
- sdr --service-url https://sdr-api-server:3000 register --label 'hey there' \
46
+ sdr register file1.png file2.png --url https://sdr-api-server:3000 \
47
+ --label 'hey there' \
28
48
  --admin-policy 'druid:bk123gh4567' \
29
49
  --collection 'druid:gh456kw9876' \
30
- --source-id 'googlebooks:stanford_12345' file1.png file2.png
50
+ --source-id 'googlebooks:stanford_12345'
31
51
  ```
32
52
 
33
53
  Deposit (register + accession) a new object:
34
54
  ```
35
- sdr --service-url https://sdr-api-server:3000 deposit --label 'hey there' \
55
+ sdr deposit file1.png file2.png --url https://sdr-api-server:3000 \
56
+ --label 'hey there' \
36
57
  --admin-policy 'druid:bk123gh4567' \
37
58
  --collection 'druid:gh456kw9876' \
38
- --source-id 'googlebooks:stanford_12345' file1.png file2.png
59
+ --source-id 'googlebooks:stanford_12345'
39
60
  ```
40
61
 
41
62
  Deposit a new object, providing metadata for files:
42
63
  ```
43
- sdr --service-url https://sdr-api-server:3000 deposit --label 'hey there' \
64
+ sdr deposit image42.jp2 ocr.html --url https://sdr-api-server:3000 \
65
+ --label 'hey there' \
44
66
  --files-metadata '{"image42.jp2":{"mime_type":"image/jp2"},"ocr.html":{"use":"transcription"}}'
45
67
  --admin-policy 'druid:bk123gh4567' \
46
68
  --collection 'druid:gh456kw9876' \
47
- --source-id 'googlebooks:stanford_12345' image42.jp2 ocr.html
69
+ --source-id 'googlebooks:stanford_12345'
48
70
  ```
49
71
 
50
72
  View the object:
51
73
  ```
52
- sdr --service-url https://sdr-api-server:3000 get druid:bw581ng3176
53
- {"type":"http://cocina.sul.stanford.edu/models/document.jsonld","externalIdentifier":"druid:bw581ng3176","label":"Something something better title","version":1,"access":{"access":"stanford","copyright":"This work is copyrighted by the creator.","download":"stanford","useAndReproductionStatement":"This document is available only to the Stanford faculty, staff and student community."},"administrative":{"hasAdminPolicy":"druid:zx485kb6348"},"description":{"title":[{"value":"Something something better title"}],"contributor":[{"name":[{"value":"Hodge, Amy"}],"type":"person","role":[{"value":"Author"},{"value":"author","uri":"http://id.loc.gov/vocabulary/relators/aut","source":{"code":"marcrelator","uri":"http://id.loc.gov/vocabulary/relators/"}},{"value":"Creator"}]}],"form":[{"structuredValue":[{"value":"Text","type":"type"},{"value":"Report","type":"subtype"}],"type":"resource type","source":{"value":"Stanford self-deposit resource types"}},{"value":"reports","type":"genre","uri":"http://vocab.getty.edu/aat/300027267","source":{"code":"aat"}},{"value":"text","type":"resource type","source":{"value":"MODS resource types"}}],"note":[{"value":";alkdfjlsadkjf;l","type":"summary"},{"value":"amyhodge@stanford.edu","type":"contact","displayLabel":"Contact"}],"subject":[{"value":"lkfj","type":"topic"},{"value":";kfj","type":"topic"},{"value":"fjwelkb","type":"topic"}]},"identification":{"sourceId":"hydrus:20"},"structural":{"contains":[{"type":"http://cocina.sul.stanford.edu/models/resources/file.jsonld","externalIdentifier":"bw581ng3176_1","label":"Test file","version":1,"structural":{"contains":[{"type":"http://cocina.sul.stanford.edu/models/file.jsonld","externalIdentifier":"druid:bw581ng3176/test.txt","label":"test.txt","filename":"test.txt","size":11,"version":1,"hasMimeType":"text/plain","hasMessageDigests":[{"type":"sha1","digest":"5d39343e4bb48abd97f759828282f5ebbac56c5e"},{"type":"md5","digest":"63b8812b0c05722a9d6c51cbd2bfb54b"}],"access":{"access":"world","download":"world"},"administrative":{"sdrPreserve":true,"shelve":true}}]}}]}}
54
- ```
55
-
56
- Display version of sdr-client:
57
- ```
58
- sdr version
74
+ sdr get druid:bw581ng3176 --url https://sdr-api-server:3000
75
+ {"type":"https://cocina.sul.stanford.edu/models/document","externalIdentifier":"druid:bw581ng3176","label":"Something something better title","version":1,"access":{"view":"stanford","copyright":"This work is copyrighted by the creator.","download":"stanford","useAndReproductionStatement":"This document is available only to the Stanford faculty, staff and student community."},"administrative":{"hasAdminPolicy":"druid:zx485kb6348"},"description":{"title":[{"value":"Something something better title"}],"contributor":[{"name":[{"value":"Hodge, Amy"}],"type":"person","role":[{"value":"Author"},{"value":"author","uri":"http://id.loc.gov/vocabulary/relators/aut","source":{"code":"marcrelator","uri":"http://id.loc.gov/vocabulary/relators/"}},{"value":"Creator"}]}],"form":[{"structuredValue":[{"value":"Text","type":"type"},{"value":"Report","type":"subtype"}],"type":"resource type","source":{"value":"Stanford self-deposit resource types"}},{"value":"reports","type":"genre","uri":"http://vocab.getty.edu/aat/300027267","source":{"code":"aat"}},{"value":"text","type":"resource type","source":{"value":"MODS resource types"}}],"note":[{"value":";alkdfjlsadkjf;l","type":"summary"},{"value":"amyhodge@stanford.edu","type":"contact","displayLabel":"Contact"}],"subject":[{"value":"lkfj","type":"topic"},{"value":";kfj","type":"topic"},{"value":"fjwelkb","type":"topic"}]},"identification":{"sourceId":"hydrus:20"},"structural":{"contains":[{"type":"https://cocina.sul.stanford.edu/models/resources/file","externalIdentifier":"bw581ng3176_1","label":"Test file","version":1,"structural":{"contains":[{"type":"https://cocina.sul.stanford.edu/models/file","externalIdentifier":"druid:bw581ng3176/test.txt","label":"test.txt","filename":"test.txt","size":11,"version":1,"hasMimeType":"text/plain","hasMessageDigests":[{"type":"sha1","digest":"5d39343e4bb48abd97f759828282f5ebbac56c5e"},{"type":"md5","digest":"63b8812b0c05722a9d6c51cbd2bfb54b"}],"access":{"view":"world","download":"world"},"administrative":{"sdrPreserve":true,"shelve":true}}]}}]}}
59
76
  ```
60
77
 
61
- Update an object, changing its admin policy object (APO):
78
+ Update an object:
62
79
  ```
63
- sdr --service-url https://sdr-api-server:3000 update --admin-policy druid:bx911tp9024 druid:bb408qn5061
80
+ # Change admin policy object (APO)
81
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --admin-policy druid:bx911tp9024
82
+ # Change collection
83
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --collection druid:pb756dt1672
84
+ # Change copyright
85
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --copyright "Here is a new copyright statement"
86
+ # Change use and reproduction statement
87
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --use-and-reproduction "Here are the terms of use..."
88
+ # Change license
89
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --license "https://www.apache.org/licenses/LICENSE-2.0"
90
+ # Change access controls
91
+ sdr update druid:bb408qn5061 --url https://sdr-api-server:3000 --view "location-based" --download "none" --location "music" --cdl false
64
92
  ```
65
93
 
66
-
67
94
  ## Testing
68
95
 
69
- To test running sdr-client against the SDR API, which itself has dependencies on other SDR services, we tend to test against our running SDR QA environment. Make sure you are connected to VPN throughout your testing, and pass `https://sdr-api-qa.stanford.edu` as the value to the `--service-url` flag for the commands above.
96
+ To test running sdr-client against the SDR API, which itself has dependencies on other SDR services, we tend to test against our running SDR QA environment. Make sure you are connected to VPN throughout your testing, and pass `https://sdr-api-qa.stanford.edu` as the value to the `--url` flag for the commands above.
70
97
 
71
- **WARNING**: if you omit the `--service-url` flag, the sdr-client CLI by default operates against the production environment.
98
+ **WARNING**: if you omit the `--url` flag, the sdr-client CLI by default operates against the production environment.
data/exe/sdr CHANGED
@@ -2,133 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  $LOAD_PATH.unshift 'lib'
5
- require 'optparse'
6
- require 'sdr_client'
7
-
8
- options = {}
9
-
10
- global = OptionParser.new do |opts|
11
- opts.on('--service-url URL', 'Connect to the host at this URL') do |url|
12
- options[:url] = url
13
- end
14
- opts.on('-h', '--help', 'Display this screen') do
15
- SdrClient::CLI.help
16
- end
17
- end
18
-
19
- global.order!
20
- command = ARGV.shift
21
-
22
- deposit_options = OptionParser.new do |opts|
23
- opts.banner = "Usage: sdr #{command} [options]"
24
- opts.on('--label LABEL', 'The object label') do |label|
25
- options[:label] = label
26
- end
27
-
28
- opts.on('--admin-policy ADMIN_POLICY', 'The druid identifier of the admin policy object') do |apo|
29
- options[:apo] = apo
30
- end
31
-
32
- opts.on('--type TYPE', 'The object type to create. ' \
33
- 'One of: "image", "book", "document", "map", "manuscript", "media", ' \
34
- '"three_dimensional", "object", "collection", or "admin_policy"') do |type|
35
- if %w[image book document map manuscript media three_dimensional object collection admin_policy].include?(type)
36
- options[:type] = "http://cocina.sul.stanford.edu/models/#{type}.jsonld"
37
- end
38
- end
39
-
40
- opts.on('--collection COLLECTION', 'The druid identifier of the collection object') do |collection|
41
- options[:collection] = collection
42
- end
43
-
44
- opts.on('--catkey CATKEY', 'The catkey for this item') do |catkey|
45
- options[:catkey] = catkey
46
- end
47
-
48
- opts.on('--source-id SOURCE_ID', 'The source id for this object') do |source_id|
49
- options[:source_id] = source_id
50
- end
51
-
52
- opts.on('--copyright COPYRIGHT', 'The copyright statement') do |copyright|
53
- options[:copyright] = copyright
54
- end
55
-
56
- opts.on('--use-statement STATEMENT', 'The use and reproduction statement') do |use_statement|
57
- options[:use_statement] = use_statement
58
- end
59
-
60
- opts.on('--viewing-direction DIRECTION', 'The viewing direction (if a book). ' \
61
- 'Either "left-to-right" or "right-to-left"') do |viewing_direction|
62
- options[:viewing_direction] = viewing_direction if %w[left-to-right right-to-left].include?(viewing_direction)
63
- end
64
5
 
65
- opts.on('--access LEVEL', 'The access level for this object. ' \
66
- 'Either "world", "stanford", "location-based", "citation-only" or "dark"') do |level|
67
- options[:access] = level if %w[world stanford location-based citation-only dark].include?(level)
68
- end
69
-
70
- opts.on('--files-metadata FILES_METADATA', 'A JSON object representing per-file metadata') do |files_metadata|
71
- options[:files_metadata] = JSON.parse(files_metadata)
72
- end
73
-
74
- opts.on('--strategy STRATEGY',
75
- 'The strategy to use for distributing files into filesets. Either "default" or "filename"') do |strategy|
76
- strategy_class = case strategy
77
- when 'filename'
78
- SdrClient::Deposit::MatchingFileGroupingStrategy
79
- when 'default'
80
- SdrClient::Deposit::SingleFileGroupingStrategy
81
- else
82
- warn "Unknown strategy #{strategy}"
83
- exit(1)
84
- end
85
- options[:grouping_strategy] = strategy_class
86
- end
87
-
88
- opts.on('-h', '--help', 'Display this screen') do
89
- puts opts
90
- exit
91
- end
92
- end
93
-
94
- update_options = OptionParser.new do |opts|
95
- opts.banner = "Usage: sdr #{command} [options]"
96
-
97
- opts.on('--admin-policy ADMIN_POLICY', 'The druid identifier of the admin policy object') do |apo|
98
- options[:apo] = apo
99
- end
100
-
101
- opts.on('-h', '--help', 'Display this screen') do
102
- puts opts
103
- exit
104
- end
105
- end
106
-
107
- SdrClient::CLI.help unless command
108
-
109
- subcommands = {
110
- 'deposit' => deposit_options,
111
- 'get' => OptionParser.new,
112
- 'login' => OptionParser.new,
113
- 'register' => deposit_options,
114
- 'update' => update_options,
115
- 'version' => OptionParser.new
116
- }
117
-
118
- unless subcommands.key?(command)
119
- puts "unknown command '#{command}'"
120
- SdrClient::CLI.help
121
- end
122
-
123
- subcommands[command].order!
124
-
125
- options[:url] ||= 'https://sdr-api-prod.stanford.edu'
6
+ require 'sdr_client'
126
7
 
127
- begin
128
- SdrClient::CLI.start(command, options, ARGV)
129
- rescue StandardError => e
130
- warn "There was a problem making your request:\n\n"
131
- warn e.message
132
- puts
133
- puts subcommands[command].help
134
- end
8
+ SdrClient::CLI.start(ARGV)
@@ -1,111 +1,172 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module SdrClient
4
- # The command line interface
5
- module CLI
6
- HELP = <<~HELP
7
- DESCRIPTION:
8
- The SDR Command Line Interface is a tool to interact with the Stanford Digital Repository.
9
-
10
- SYNOPSIS:
11
- sdr [options] <command>
12
-
13
- To see help text for each command you can run:
14
-
15
- sdr [options] <command> help
16
-
17
- OPTIONS:
18
- --service-url (string)
19
- Override the command's default URL with the given URL. Default: https://sdr-api-prod.stanford.edu
20
-
21
- -h, --help
22
- Displays this screen
3
+ require 'thor'
23
4
 
5
+ module SdrClient
6
+ # The SDR command-line interface
7
+ class CLI < Thor
8
+ include Thor::Actions
9
+
10
+ # Make sure Thor commands preserve exit statuses
11
+ # @see https://github.com/rails/thor/wiki/Making-An-Executable
12
+ def self.exit_on_failure?
13
+ true
14
+ end
24
15
 
25
- COMMANDS:
26
- get
27
- Retrieve an object from the SDR
16
+ # Print out help and exit with error code if command not found
17
+ def self.handle_no_command_error(command)
18
+ puts "Command '#{command}' not found, displaying help:"
19
+ puts
20
+ puts help
21
+ exit(1)
22
+ end
28
23
 
29
- deposit
30
- Accession an object into the SDR
24
+ def self.default_url
25
+ 'https://sdr-api-prod.stanford.edu'
26
+ end
31
27
 
32
- register
33
- Create a draft object in the SDR and retrieve a Druid identifier
28
+ package_name 'sdr'
34
29
 
35
- update
36
- Update an object in the SDR
30
+ class_option :url, desc: 'URL of SDR API endpoint', type: :string, default: default_url
37
31
 
38
- login
39
- Will prompt for email & password and exchange it for an login token (saved in ~/.sdr/token)
32
+ desc 'get DRUID', 'Retrieve an object from the SDR'
33
+ def get(druid)
34
+ say SdrClient::Find.run(druid, url: options[:url])
35
+ rescue SdrClient::Credentials::NoCredentialsError
36
+ say_error 'Log in first'
37
+ exit(1)
38
+ end
40
39
 
41
- version
42
- Display the sdr-client version
40
+ desc 'login', 'Prompt for email & password and create a login token (saved in ~/.sdr/token)'
41
+ def login
42
+ status = SdrClient::Login.run(
43
+ url: options[:url],
44
+ login_service: lambda do
45
+ {
46
+ email: ask('Email:'),
47
+ password: ask('Password:', echo: false)
48
+ }
49
+ end
50
+ )
51
+ return puts unless status.failure?
43
52
 
44
- HELP
53
+ say_error status.failure
54
+ exit(1)
55
+ end
45
56
 
46
- def self.start(command, options, arguments = [])
47
- case command
48
- when 'get'
49
- puts SdrClient::Find.run(arguments.first, **options)
50
- when 'deposit', 'register'
51
- deposit(command, options, arguments)
52
- when 'update'
53
- if arguments.size != 1 || !arguments.first.is_a?(String)
54
- raise "Expected a single druid argument, received #{arguments}"
55
- end
57
+ desc 'version', 'Display the SDR CLI version'
58
+ def version
59
+ say SdrClient::VERSION
60
+ end
56
61
 
57
- job_id = SdrClient::Update.run(arguments.first, **options)
58
- poll_for_job_complete(job_id: job_id, url: options[:url]) # TODO: add an option that skips this
59
- when 'login'
60
- status = SdrClient::Login.run(**options)
61
- puts status.failure if status.failure?
62
- when 'version'
63
- puts SdrClient::VERSION
64
- else
65
- raise "Unknown command #{command}"
66
- end
62
+ desc 'update DRUID', 'Update an object in the SDR'
63
+ option :skip_polling, type: :boolean, default: false, aliases: '-s', desc: 'Print out job ID instead of polling for result'
64
+ option :apo, desc: 'Druid identifier of the admin policy object', aliases: '--admin-policy'
65
+ option :collection, desc: 'Druid identifier of the collection object'
66
+ option :copyright, desc: 'Copyright statement'
67
+ option :use_and_reproduction, desc: 'Use and reproduction statement'
68
+ option :license, desc: 'License URI'
69
+ option :view, enum: %w[world stanford location-based citation-only dark], desc: 'Access view level for the object'
70
+ option :download, enum: %w[world stanford location-based none], desc: 'Access download level for the object'
71
+ option :location, enum: %w[spec music ars art hoover m&m], desc: 'Access location for the object'
72
+ option :cdl, type: :boolean, default: false
73
+ def update(druid)
74
+ validate_druid!(druid)
75
+ job_id = SdrClient::Update.run(druid, **options)
76
+ poll_for_job_complete(job_id: job_id, url: options[:url]) # TODO: add an option that skips this
67
77
  rescue SdrClient::Credentials::NoCredentialsError
68
- puts 'Log in first'
78
+ say_error 'Log in first'
69
79
  exit(1)
70
80
  end
71
81
 
72
- def self.deposit(command, options, arguments)
73
- options[:files] = arguments if arguments.present?
74
- display_errors(validate_deposit_options(options))
75
- job_id = SdrClient::Deposit.run(accession: command == 'deposit', **options)
76
- poll_for_job_complete(job_id: job_id, url: options[:url]) # TODO: add an option that skips this
82
+ desc 'deposit OPTIONAL_FILES', 'Deposit (accession) an object into the SDR'
83
+ option :skip_polling, type: :boolean, default: false, aliases: '-s', desc: 'Print out job ID instead of polling for result'
84
+ option :apo, required: true, desc: 'Druid identifier of the admin policy object', aliases: '--admin-policy'
85
+ option :source_id, required: true, desc: 'Source ID for this object'
86
+ option :label, desc: 'Object label'
87
+ option :type, enum: %w[image book document map manuscript media three_dimensional object collection admin_policy], desc: 'The object type'
88
+ option :collection, desc: 'Druid identifier of the collection object'
89
+ option :catkey, desc: 'Catkey for this item'
90
+ option :copyright, desc: 'Copyright statement'
91
+ option :use_and_reproduction, desc: 'Use and reproduction statement'
92
+ option :viewing_direction, enum: %w[left-to-right right-to-left], desc: 'Viewing direction (if a book)'
93
+ option :view, enum: %w[world stanford location-based citation-only dark], desc: 'Access view level for the object'
94
+ option :files_metadata, desc: 'JSON string representing per-file metadata'
95
+ option :grouping_strategy, enum: %w[default filename], desc: 'Strategy for grouping files into filesets'
96
+ def deposit(*files)
97
+ register_or_deposit(files: files, accession: true)
77
98
  end
78
99
 
79
- def self.display_errors(errors)
80
- return if errors.empty?
100
+ desc 'register OPTIONAL_FILES', 'Create a draft object in the SDR and retrieve a Druid identifier'
101
+ option :skip_polling, type: :boolean, default: false, aliases: '-s', desc: 'Print out job ID instead of polling for result'
102
+ option :apo, required: true, desc: 'Druid identifier of the admin policy object', aliases: '--admin-policy'
103
+ option :source_id, required: true, desc: 'Source ID for this object'
104
+ option :label, desc: 'Object label'
105
+ option :type, enum: %w[image book document map manuscript media three_dimensional object collection admin_policy], desc: 'The object type'
106
+ option :collection, desc: 'Druid identifier of the collection object'
107
+ option :catkey, desc: 'Catkey for this item'
108
+ option :copyright, desc: 'Copyright statement'
109
+ option :use_and_reproduction, desc: 'Use and reproduction statement'
110
+ option :viewing_direction, enum: %w[left-to-right right-to-left], desc: 'Viewing direction (if a book)'
111
+ option :view, enum: %w[world stanford location-based citation-only dark], desc: 'Access view level for the object'
112
+ option :files_metadata, desc: 'JSON string representing per-file metadata'
113
+ option :grouping_strategy, enum: %w[default filename], desc: 'Strategy for grouping files into filesets'
114
+ def register(*files)
115
+ register_or_deposit(files: files, accession: false)
116
+ end
81
117
 
82
- raise errors.map { |k, v| "#{k} #{v}" }.join("\n")
118
+ private
119
+
120
+ def register_or_deposit(files:, accession:)
121
+ opts = munge_options(options, files)
122
+ skip_polling = opts.delete(:skip_polling)
123
+ job_id = SdrClient::Deposit.run(accession: accession, **opts)
124
+ return if skip_polling
125
+
126
+ poll_for_job_complete(job_id: job_id, url: opts[:url])
127
+ rescue SdrClient::Credentials::NoCredentialsError
128
+ say_error 'Log in first'
129
+ exit(1)
83
130
  end
84
131
 
85
- def self.validate_deposit_options(options)
86
- {}.tap do |errors|
87
- errors['admin-policy'] = 'is a required argument' unless options[:apo]
88
- errors['source-id'] = 'is a required argument' unless options[:source_id]
132
+ def munge_options(options, files)
133
+ options.to_h.symbolize_keys.tap do |opts|
134
+ opts[:files] = files if files.present?
135
+ opts[:type] = Cocina::Models::ObjectType.public_send(options[:type]) if options[:type]
136
+ opts[:files_metadata] = JSON.parse(options[:files_metadata]) if options[:files_metadata]
137
+ if options[:grouping_strategy]
138
+ opts[:grouping_strategy] = if options[:grouping_strategy] == 'filename'
139
+ SdrClient::Deposit::MatchingFileGroupingStrategy
140
+ else
141
+ SdrClient::Deposit::SingleFileGroupingStrategy
142
+ end
143
+ end
89
144
  end
90
145
  end
91
146
 
92
- def self.help
93
- puts HELP
94
- exit
147
+ def validate_druid!(druid)
148
+ return if druid.present?
149
+
150
+ say_error "Not a valid druid: #{druid.inspect}"
151
+ exit(1)
95
152
  end
96
153
 
97
- def self.poll_for_job_complete(job_id:, url:)
154
+ def poll_for_job_complete(job_id:, url:)
155
+ # the extra args to `say` prevent appending a newline
156
+ say('SDR is processing your request', nil, false)
98
157
  result = nil
99
- 1.upto(5) do |_n|
158
+ (1).upto(60) do
100
159
  result = SdrClient::BackgroundJobResults.show(url: url, job_id: job_id)
101
- break unless %w[pending processing].include? result['status']
160
+ break unless %w[pending processing].include?(result['status'])
102
161
 
103
- sleep 5
162
+ # the extra args to `say` prevent appending a newline
163
+ say('.', nil, false)
164
+ sleep 1
104
165
  end
105
166
  if result['status'] == 'complete'
106
- puts result.dig('output', 'druid')
167
+ say " success! (druid: #{result.dig('output', 'druid')})"
107
168
  else
108
- warn "Job #{job_id} did not complete\n#{result.inspect}"
169
+ say_error "Job #{job_id} did not complete\n#{result.inspect}"
109
170
  end
110
171
  end
111
172
  end
@@ -6,16 +6,16 @@ module SdrClient
6
6
  class File
7
7
  # rubocop:disable Metrics/ParameterLists
8
8
  def initialize(external_identifier:, label:, filename:,
9
- access: 'dark', download: 'none', preserve: true, shelve: true,
9
+ view: 'dark', download: 'none', preserve: true, shelve: true,
10
10
  publish: true, mime_type: nil, md5: nil, sha1: nil,
11
11
  use: nil)
12
12
  @external_identifier = external_identifier
13
13
  @label = label
14
14
  @filename = filename
15
- @access = access
15
+ @view = view
16
16
  @download = download
17
17
  @preserve = preserve
18
- @shelve = access == 'dark' ? false : shelve
18
+ @shelve = view == 'dark' ? false : shelve
19
19
  @publish = publish
20
20
  @mime_type = mime_type
21
21
  @md5 = md5
@@ -31,7 +31,7 @@ module SdrClient
31
31
  filename: @filename,
32
32
  externalIdentifier: @external_identifier,
33
33
  access: {
34
- view: @access,
34
+ view: @view,
35
35
  download: @download
36
36
  },
37
37
  administrative: {
@@ -13,9 +13,9 @@ module SdrClient
13
13
  # Additional metadata includes access, preserve, shelve, publish, md5, sha1
14
14
  # rubocop:disable Metrics/ParameterLists
15
15
  def initialize(label: nil,
16
- access: 'dark',
16
+ view: 'dark',
17
17
  download: 'none',
18
- use_statement: nil,
18
+ use_and_reproduction: nil,
19
19
  copyright: nil,
20
20
  apo:,
21
21
  collection: nil,
@@ -36,9 +36,9 @@ module SdrClient
36
36
  @embargo_release_date = embargo_release_date
37
37
  @embargo_access = embargo_access
38
38
  @embargo_download = embargo_download
39
- @access = access
39
+ @view = view
40
40
  @download = download
41
- @use_statement = use_statement
41
+ @use_and_reproduction = use_and_reproduction
42
42
  @copyright = copyright
43
43
  @apo = apo
44
44
  @file_sets = file_sets
@@ -62,7 +62,7 @@ module SdrClient
62
62
  # @return [Request] a clone of this request with the file_sets added
63
63
  def with_file_sets(file_sets)
64
64
  Request.new(label: label,
65
- access: access,
65
+ view: view,
66
66
  download: download,
67
67
  apo: apo,
68
68
  collection: collection,
@@ -73,7 +73,7 @@ module SdrClient
73
73
  embargo_access: embargo_access,
74
74
  embargo_download: embargo_download,
75
75
  type: type,
76
- use_statement: use_statement,
76
+ use_and_reproduction: use_and_reproduction,
77
77
  viewing_direction: viewing_direction,
78
78
  file_sets: file_sets,
79
79
  files_metadata: files_metadata)
@@ -83,7 +83,7 @@ module SdrClient
83
83
  # @return [Hash] the metadata for the file
84
84
  def for(filename)
85
85
  metadata = files_metadata.fetch(filename, {}).with_indifferent_access
86
- metadata[:access] = access unless metadata.key?(:access)
86
+ metadata[:view] = view unless metadata.key?(:view)
87
87
  metadata[:download] = download unless metadata.key?(:download)
88
88
  metadata
89
89
  end
@@ -92,9 +92,9 @@ module SdrClient
92
92
 
93
93
  private
94
94
 
95
- attr_reader :access, :label, :file_sets, :source_id, :catkey, :apo, :collection,
95
+ attr_reader :view, :label, :file_sets, :source_id, :catkey, :apo, :collection,
96
96
  :files_metadata, :embargo_release_date, :embargo_access, :embargo_download,
97
- :viewing_direction, :use_statement, :copyright, :download
97
+ :viewing_direction, :use_and_reproduction, :copyright, :download
98
98
 
99
99
  def administrative
100
100
  {
@@ -118,10 +118,10 @@ module SdrClient
118
118
 
119
119
  def access_struct
120
120
  {
121
- view: access,
121
+ view: view,
122
122
  download: download
123
123
  }.tap do |json|
124
- json[:useAndReproductionStatement] = use_statement if use_statement
124
+ json[:useAndReproductionStatement] = use_and_reproduction if use_and_reproduction
125
125
  json[:copyright] = copyright if copyright
126
126
 
127
127
  if embargo_release_date
@@ -12,9 +12,9 @@ module SdrClient
12
12
  def self.run(label: nil,
13
13
  type: BOOK_TYPE,
14
14
  viewing_direction: nil,
15
- access: 'dark',
15
+ view: 'dark',
16
16
  download: 'none',
17
- use_statement: nil,
17
+ use_and_reproduction: nil,
18
18
  copyright: nil,
19
19
  apo:,
20
20
  collection: nil,
@@ -33,10 +33,10 @@ module SdrClient
33
33
  augmented_metadata = FileMetadataBuilder.build(files: files, files_metadata: files_metadata)
34
34
  metadata = Request.new(label: label,
35
35
  type: type,
36
- access: access,
36
+ view: view,
37
37
  download: download,
38
38
  apo: apo,
39
- use_statement: use_statement,
39
+ use_and_reproduction: use_and_reproduction,
40
40
  copyright: copyright,
41
41
  collection: collection,
42
42
  source_id: source_id,
@@ -8,7 +8,9 @@ module SdrClient
8
8
 
9
9
  # @return [Result] the status of the call
10
10
  def self.run(url:, login_service: LoginPrompt, credential_store: Credentials)
11
- request_json = JSON.generate(login_service.run)
11
+ request_json = JSON.generate(
12
+ login_service.respond_to?(:run) ? login_service.run : login_service.call
13
+ )
12
14
  response = Faraday.post(url + LOGIN_PATH, request_json, 'Content-Type' => 'application/json')
13
15
  case response.status
14
16
  when 200
@@ -2,24 +2,139 @@
2
2
 
3
3
  module SdrClient
4
4
  # The namespace for the "update" command
5
- module Update
5
+ class Update
6
6
  # @return [String] job id for the background job result
7
- def self.run(druid, apo:, url:, logger: Logger.new($stdout))
8
- cocina_item = Cocina::Models.build(
7
+ def self.run(druid, **options)
8
+ new(druid, **options).run
9
+ end
10
+
11
+ def initialize(druid, **options)
12
+ @druid = druid
13
+ @url = options.fetch(:url)
14
+ @options = options
15
+ end
16
+
17
+ # @return [String] job id for the background job result
18
+ def run
19
+ SdrClient::Deposit::UpdateResource.run(
20
+ metadata: updated_cocina_item,
21
+ logger: options[:logger] || Logger.new($stdout),
22
+ connection: SdrClient::Connection.new(url: url)
23
+ )
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :druid, :logger, :options, :url
29
+
30
+ def updated_cocina_item
31
+ @updated_cocina_item ||=
32
+ original_cocina_item.then { |cocina_item| update_apo(cocina_item) }
33
+ .then { |cocina_item| update_collection(cocina_item) }
34
+ .then { |cocina_item| update_copyright(cocina_item) }
35
+ .then { |cocina_item| update_use_and_reproduction(cocina_item) }
36
+ .then { |cocina_item| update_license(cocina_item) }
37
+ .then { |cocina_item| update_access(cocina_item) }
38
+ end
39
+
40
+ def original_cocina_item
41
+ Cocina::Models.build(
9
42
  JSON.parse(
10
43
  SdrClient::Find.run(druid, url: url)
11
44
  )
12
45
  )
46
+ end
13
47
 
14
- SdrClient::Deposit::UpdateResource.run(
15
- metadata: cocina_item.new(
16
- administrative: cocina_item.administrative.new(
17
- hasAdminPolicy: apo
18
- )
48
+ # Update the APO of a Cocina item if the options specify a new one, else return the original
49
+ def update_apo(cocina_item)
50
+ return cocina_item unless options[:apo]
51
+
52
+ cocina_item.new(
53
+ administrative: cocina_item.administrative.new(
54
+ hasAdminPolicy: options[:apo]
55
+ )
56
+ )
57
+ end
58
+
59
+ # Update the collection of a Cocina item if the options specify a new one, else return the original
60
+ def update_collection(cocina_item)
61
+ return cocina_item unless options[:collection]
62
+
63
+ cocina_item.new(
64
+ structural: cocina_item.structural.new(
65
+ isMemberOf: Array(options[:collection])
66
+ )
67
+ )
68
+ end
69
+
70
+ # Update the copyright of a Cocina item if the options specify a new one, else return the original
71
+ def update_copyright(cocina_item)
72
+ return cocina_item unless options[:copyright]
73
+
74
+ cocina_item.new(
75
+ access: cocina_item.access.new(
76
+ copyright: options[:copyright]
77
+ )
78
+ )
79
+ end
80
+
81
+ # Update the use and reproduction statement of a Cocina item if the options specify a new one, else return the original
82
+ def update_use_and_reproduction(cocina_item)
83
+ return cocina_item unless options[:use_and_reproduction]
84
+
85
+ cocina_item.new(
86
+ access: cocina_item.access.new(
87
+ useAndReproductionStatement: options[:use_and_reproduction]
88
+ )
89
+ )
90
+ end
91
+
92
+ # Update the license of a Cocina item if the options specify a new one, else return the original
93
+ def update_license(cocina_item)
94
+ return cocina_item unless options[:license]
95
+
96
+ cocina_item.new(
97
+ access: cocina_item.access.new(
98
+ license: options[:license]
99
+ )
100
+ )
101
+ end
102
+
103
+ # rubocop:disable Style/DoubleNegation
104
+ # Update the access of a Cocina item if the options specify a new one, else return the original
105
+ def update_access(cocina_item)
106
+ return cocina_item unless options[:view] || options[:download] || options[:location] || options[:cdl]
107
+
108
+ cocina_item.new(
109
+ access: cocina_item.access.new(
110
+ view: options[:view],
111
+ download: options[:download],
112
+ location: options[:location],
113
+ controlledDigitalLending: !!options[:cdl]
19
114
  ),
20
- logger: logger,
21
- connection: Connection.new(url: url)
115
+ structural: cocina_item.structural.new(
116
+ contains: cocina_item.structural.contains.map do |file_set|
117
+ file_set.new(
118
+ structural: file_set.structural.new(
119
+ contains: file_set.structural.contains.map do |file|
120
+ file.new(
121
+ access: file.access.new(
122
+ view: options[:view],
123
+ download: options[:download],
124
+ location: options[:location],
125
+ controlledDigitalLending: !!options[:cdl]
126
+ ),
127
+ administrative: options[:view] == 'dark' ?
128
+ { publish: false, shelve: false, sdrPreserve: file.administrative.sdrPreserve } :
129
+ file.administrative
130
+ )
131
+ end
132
+ )
133
+ )
134
+ end
135
+ )
22
136
  )
23
137
  end
138
+ # rubocop:enable Style/DoubleNegation
24
139
  end
25
140
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SdrClient
4
- VERSION = '0.71.0'
4
+ VERSION = '0.72.0'
5
5
  end
data/sdr-client.gemspec CHANGED
@@ -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.68.0'
31
+ spec.add_dependency 'cocina-models', '~> 0.69.0'
32
32
  spec.add_dependency 'dry-monads'
33
33
  spec.add_dependency 'faraday', '>= 0.16'
34
34
 
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_development_dependency 'rubocop-rake'
40
40
  spec.add_development_dependency 'rubocop-rspec', '~> 2.1'
41
41
  spec.add_development_dependency 'simplecov'
42
+ spec.add_development_dependency 'thor'
42
43
  spec.add_development_dependency 'webmock', '~> 3.7'
43
44
  spec.metadata['rubygems_mfa_required'] = 'true'
44
45
  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.71.0
4
+ version: 0.72.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-09 00:00:00.000000000 Z
11
+ date: 2022-03-15 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.68.0
33
+ version: 0.69.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.68.0
40
+ version: 0.69.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: dry-monads
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: thor
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: webmock
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -242,7 +256,7 @@ metadata:
242
256
  source_code_uri: https://github.com/sul-dlss/sdr-client
243
257
  changelog_uri: https://github.com/sul-dlss/sdr-client/releases
244
258
  rubygems_mfa_required: 'true'
245
- post_install_message:
259
+ post_install_message:
246
260
  rdoc_options: []
247
261
  require_paths:
248
262
  - lib
@@ -258,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
272
  version: '0'
259
273
  requirements: []
260
274
  rubygems_version: 3.2.32
261
- signing_key:
275
+ signing_key:
262
276
  specification_version: 4
263
277
  summary: The CLI for https://github.com/sul-dlss/sdr-api
264
278
  test_files: []