scholarsphere-client 0.1.1 → 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: 84b082e1c474c137dd8202b5ae6666eb73938f9b1bd806f053308a715272c28f
4
- data.tar.gz: c94c0532b48c3b0f9f3b9918f01275a572f75a8ff3f7d5112837b7fd78e474ad
3
+ metadata.gz: 8ab004f36213538c0e74821396c0499a52f08a24c8a8d7f450b3c224a0b71a8d
4
+ data.tar.gz: 84788832717be95ff66b8def2433633fc41a82aa85e4b204d1750814b8fa4bb4
5
5
  SHA512:
6
- metadata.gz: 517ddf088ccad9da2fbdae7b0d674c3017c5085e850d568f23fa0eaff95899e4bfe728de6f144f2480d09d65025801c22e0929eeb60bf3548c7613e041b40cb8
7
- data.tar.gz: a7b2df6784f547a3ceb9b9de3e82497398e76b794dfe0fe22cdbbc6214e7a068940b8df7957195b61a417f9f8d4f4b1dd115693630467661b821907675b32b94
6
+ metadata.gz: 45c9c56b6f2db7ed715a595b9d5533ddba125b0a63d7b2d694669105c50b65e19c66b70814add3921b1d1fc83ef1ebc6592488641206c1eef1ef21c331d9649c
7
+ data.tar.gz: c7d927e3d027ecdf2b204f350b0758b353d0b161e285b795111174deca41951c2ca8f5642cc6461852b831cd6a1c890febb5083730b08b3360358fbbf902c574
@@ -0,0 +1,40 @@
1
+ version: 2.1
2
+ orbs:
3
+ ruby: circleci/ruby@0.1.2
4
+
5
+ jobs:
6
+ build:
7
+ docker:
8
+ - image: circleci/ruby:2.6.3-stretch-node
9
+ executor: ruby/default
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: "Install Dependencides"
14
+ command: gem install bundler:2.1.4 && bundle install
15
+ - run:
16
+ name: "Rubocop"
17
+ command: bundle exec rubocop
18
+ - run:
19
+ name: "RSpec"
20
+ command: bundle exec rspec
21
+ environment:
22
+ SS4_ENDPOINT: "https://scholarsphere.test/api/v1"
23
+ - run:
24
+ name: "Copy VCR Logs"
25
+ when: on_fail
26
+ command: cp vcr.log /tmp/vcr.log
27
+ - run:
28
+ name: "Upload Coverage"
29
+ when: on_success
30
+ command: |
31
+ wget -q https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 -O cc-test-reporter
32
+ chmod 755 cc-test-reporter
33
+ export TAG=${CIRCLE_SHA1}
34
+ export GIT_COMMIT_SHA=$CIRCLE_SHA1
35
+ export GIT_BRANCH=$CIRCLE_BRANCH
36
+ export GIT_COMMITED_AT=$(git log -1 --date=short --pretty=format:%ct)
37
+ ./cc-test-reporter after-build -d
38
+ - store_artifacts:
39
+ path: /tmp/vcr.log
40
+ destination: VCR
data/.gitignore CHANGED
@@ -12,4 +12,4 @@
12
12
 
13
13
  # Ignore application configuration
14
14
  /config/scholarsphere-client.yml
15
- Gemfile.lock
15
+ vcr.log
data/.rubocop.yml CHANGED
@@ -11,3 +11,6 @@ Metrics/BlockLength:
11
11
  Gemspec/RequiredRubyVersion:
12
12
  Exclude:
13
13
  - 'scholarsphere-client.gemspec'
14
+
15
+ RSpec/MultipleMemoizedHelpers:
16
+ Enabled: false
data/Gemfile.lock ADDED
@@ -0,0 +1,204 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scholarsphere-client (0.2.0)
5
+ aws-sdk-s3 (~> 1.49)
6
+ faraday (> 0.12)
7
+ marcel (~> 0.3)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actionview (6.1.1)
13
+ activesupport (= 6.1.1)
14
+ builder (~> 3.1)
15
+ erubi (~> 1.4)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
18
+ activesupport (6.1.1)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (>= 1.6, < 2)
21
+ minitest (>= 5.1)
22
+ tzinfo (~> 2.0)
23
+ zeitwerk (~> 2.3)
24
+ addressable (2.7.0)
25
+ public_suffix (>= 2.0.2, < 5.0)
26
+ ast (2.4.1)
27
+ aws-eventstream (1.1.0)
28
+ aws-partitions (1.322.0)
29
+ aws-sdk-core (3.97.0)
30
+ aws-eventstream (~> 1, >= 1.0.2)
31
+ aws-partitions (~> 1, >= 1.239.0)
32
+ aws-sigv4 (~> 1.1)
33
+ jmespath (~> 1.0)
34
+ aws-sdk-kms (1.32.0)
35
+ aws-sdk-core (~> 3, >= 3.71.0)
36
+ aws-sigv4 (~> 1.1)
37
+ aws-sdk-s3 (1.67.0)
38
+ aws-sdk-core (~> 3, >= 3.96.1)
39
+ aws-sdk-kms (~> 1)
40
+ aws-sigv4 (~> 1.1)
41
+ aws-sigv4 (1.1.4)
42
+ aws-eventstream (~> 1.0, >= 1.0.2)
43
+ better_html (1.0.15)
44
+ actionview (>= 4.0)
45
+ activesupport (>= 4.0)
46
+ ast (~> 2.0)
47
+ erubi (~> 1.4)
48
+ html_tokenizer (~> 0.0.6)
49
+ parser (>= 2.4)
50
+ smart_properties
51
+ builder (3.2.4)
52
+ byebug (11.1.3)
53
+ coderay (1.1.3)
54
+ colorize (0.8.1)
55
+ concurrent-ruby (1.1.7)
56
+ crack (0.4.5)
57
+ rexml
58
+ crass (1.0.6)
59
+ diff-lcs (1.3)
60
+ docile (1.3.5)
61
+ erb_lint (0.0.37)
62
+ activesupport
63
+ better_html (~> 1.0.7)
64
+ html_tokenizer
65
+ parser (>= 2.7.1.4)
66
+ rainbow
67
+ rubocop
68
+ smart_properties
69
+ erubi (1.10.0)
70
+ faraday (1.0.1)
71
+ multipart-post (>= 1.2, < 3)
72
+ ffi (1.14.2)
73
+ hashdiff (1.0.1)
74
+ html_tokenizer (0.0.7)
75
+ i18n (1.8.7)
76
+ concurrent-ruby (~> 1.0)
77
+ jmespath (1.4.0)
78
+ json (2.5.1)
79
+ loofah (2.8.0)
80
+ crass (~> 1.0.2)
81
+ nokogiri (>= 1.5.9)
82
+ marcel (0.3.3)
83
+ mimemagic (~> 0.3.2)
84
+ method_source (1.0.0)
85
+ mimemagic (0.3.5)
86
+ mini_portile2 (2.5.0)
87
+ minitest (5.14.3)
88
+ multipart-post (2.1.1)
89
+ niftany (0.8.0)
90
+ colorize (~> 0.8.1)
91
+ erb_lint (~> 0.0.22)
92
+ rubocop (~> 0.79)
93
+ rubocop-performance (~> 1.1)
94
+ rubocop-rails (~> 2.3)
95
+ rubocop-rspec (~> 1.3)
96
+ scss_lint (~> 0.55)
97
+ nokogiri (1.11.1)
98
+ mini_portile2 (~> 2.5.0)
99
+ racc (~> 1.4)
100
+ parallel (1.20.1)
101
+ parser (3.0.0.0)
102
+ ast (~> 2.4.1)
103
+ pry (0.13.1)
104
+ coderay (~> 1.1)
105
+ method_source (~> 1.0)
106
+ pry-byebug (3.9.0)
107
+ byebug (~> 11.0)
108
+ pry (~> 0.13.0)
109
+ public_suffix (4.0.6)
110
+ racc (1.5.2)
111
+ rack (2.2.3)
112
+ rails-dom-testing (2.0.3)
113
+ activesupport (>= 4.2.0)
114
+ nokogiri (>= 1.6)
115
+ rails-html-sanitizer (1.3.0)
116
+ loofah (~> 2.3)
117
+ rainbow (3.0.0)
118
+ rake (13.0.1)
119
+ rb-fsevent (0.10.4)
120
+ rb-inotify (0.10.1)
121
+ ffi (~> 1.0)
122
+ regexp_parser (2.0.3)
123
+ rexml (3.2.4)
124
+ rspec (3.9.0)
125
+ rspec-core (~> 3.9.0)
126
+ rspec-expectations (~> 3.9.0)
127
+ rspec-mocks (~> 3.9.0)
128
+ rspec-core (3.9.2)
129
+ rspec-support (~> 3.9.3)
130
+ rspec-expectations (3.9.2)
131
+ diff-lcs (>= 1.2.0, < 2.0)
132
+ rspec-support (~> 3.9.0)
133
+ rspec-its (1.3.0)
134
+ rspec-core (>= 3.0.0)
135
+ rspec-expectations (>= 3.0.0)
136
+ rspec-mocks (3.9.1)
137
+ diff-lcs (>= 1.2.0, < 2.0)
138
+ rspec-support (~> 3.9.0)
139
+ rspec-support (3.9.3)
140
+ rubocop (0.93.1)
141
+ parallel (~> 1.10)
142
+ parser (>= 2.7.1.5)
143
+ rainbow (>= 2.2.2, < 4.0)
144
+ regexp_parser (>= 1.8)
145
+ rexml
146
+ rubocop-ast (>= 0.6.0)
147
+ ruby-progressbar (~> 1.7)
148
+ unicode-display_width (>= 1.4.0, < 2.0)
149
+ rubocop-ast (1.4.0)
150
+ parser (>= 2.7.1.5)
151
+ rubocop-performance (1.9.2)
152
+ rubocop (>= 0.90.0, < 2.0)
153
+ rubocop-ast (>= 0.4.0)
154
+ rubocop-rails (2.9.1)
155
+ activesupport (>= 4.2.0)
156
+ rack (>= 1.1)
157
+ rubocop (>= 0.90.0, < 2.0)
158
+ rubocop-rspec (1.44.1)
159
+ rubocop (~> 0.87)
160
+ rubocop-ast (>= 0.7.1)
161
+ ruby-progressbar (1.11.0)
162
+ sass (3.7.4)
163
+ sass-listen (~> 4.0.0)
164
+ sass-listen (4.0.0)
165
+ rb-fsevent (~> 0.9, >= 0.9.4)
166
+ rb-inotify (~> 0.9, >= 0.9.7)
167
+ scss_lint (0.59.0)
168
+ sass (~> 3.5, >= 3.5.5)
169
+ simplecov (0.17.1)
170
+ docile (~> 1.1)
171
+ json (>= 1.8, < 3)
172
+ simplecov-html (~> 0.10.0)
173
+ simplecov-html (0.10.2)
174
+ smart_properties (1.15.0)
175
+ tzinfo (2.0.4)
176
+ concurrent-ruby (~> 1.0)
177
+ unicode-display_width (1.7.0)
178
+ vcr (6.0.0)
179
+ webmock (3.11.1)
180
+ addressable (>= 2.3.6)
181
+ crack (>= 0.3.2)
182
+ hashdiff (>= 0.4.0, < 2.0.0)
183
+ yard (0.9.26)
184
+ zeitwerk (2.4.2)
185
+
186
+ PLATFORMS
187
+ ruby
188
+
189
+ DEPENDENCIES
190
+ bundler (~> 2.0)
191
+ niftany (~> 0.6)
192
+ pry (~> 0.12)
193
+ pry-byebug (~> 3.9)
194
+ rake (>= 12.3.3)
195
+ rspec (~> 3.0)
196
+ rspec-its (~> 1.3)
197
+ scholarsphere-client!
198
+ simplecov (< 0.18)
199
+ vcr (~> 6.0)
200
+ webmock (~> 3.11)
201
+ yard (< 1.0)
202
+
203
+ BUNDLED WITH
204
+ 2.1.4
data/README.md CHANGED
@@ -20,8 +20,60 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- You can read the [ruby docs](https://www.rubydoc.info/gems/scholarsphere-client) for the latest features.
23
+ ### Authentication
24
24
 
25
- **Warning!**
25
+ Obtain an api key, and save it to `config/scholarsphere-client.yml`
26
26
 
27
- This is not yet in 1.0 status and features will change without the customary deprecation warnings.
27
+ SS4_ENDPOINT: "http://scholarsphere/api/v1"
28
+ SS_CLIENT_KEY: "[key]"
29
+
30
+ If you are using a testing instance, you'll need to disable ssl verification:
31
+
32
+ SS_CLIENT_SSL: "false"
33
+
34
+ ### Ingesting
35
+
36
+ To publish a work:
37
+
38
+ metadata = {
39
+ title: "My Awesome Work",
40
+ creators_attributes: [
41
+ {
42
+ display_name: 'Dr. Pat Researcher',
43
+ actor_attributes: {
44
+ psu_id: 'pxr123',
45
+ surname: 'Researcher',
46
+ given_name: 'Pat',
47
+ email: 'pxr123@psu.edu'
48
+ }
49
+ }
50
+ ]
51
+ }
52
+
53
+ files = [ File.new('path/to/file') ]
54
+
55
+ depositor = {
56
+ psu_id: 'pxr123',
57
+ surname: 'Researcher',
58
+ given_name: 'Pat',
59
+ email: 'pxr123@psu.edu'
60
+ }
61
+
62
+ ingest = Scholarsphere::Client::Ingest.new(
63
+ metadata: metadata,
64
+ files: files,
65
+ depositor: depositor
66
+ )
67
+
68
+ response = ingest.publish
69
+
70
+ puts response.body
71
+
72
+ {
73
+ "message": "Work was successfully created",
74
+ "url": "/resources/0797e99c-7d4f-4e05-8bf6-86aea1029a6a"
75
+ }
76
+
77
+ ## Documentation
78
+
79
+ You can read the [ruby docs](https://www.rubydoc.info/github/psu-stewardship/scholarsphere-client/main) for the latest features.
@@ -7,6 +7,7 @@ require 'scholarsphere/s3'
7
7
  require 'scholarsphere/client/config'
8
8
  require 'scholarsphere/client/ingest'
9
9
  require 'scholarsphere/client/collection'
10
+ require 'scholarsphere/client/upload'
10
11
  require 'scholarsphere/client/version'
11
12
 
12
13
  module Scholarsphere
@@ -16,6 +17,7 @@ module Scholarsphere
16
17
  Config.load_defaults
17
18
 
18
19
  class << self
20
+ # @return [Faraday::Connection] A cached connection to the Scholarsphere API with the provided credentials.
19
21
  def connection
20
22
  @connection ||= Faraday::Connection.new(
21
23
  url: ENV['SS4_ENDPOINT'],
@@ -27,10 +29,18 @@ module Scholarsphere
27
29
  )
28
30
  end
29
31
 
32
+ # @return [nil] Resets the client connection when needed.
33
+ def reset
34
+ @connection = nil
35
+ end
36
+
37
+ # @return [TrueClass, FalseClass] If set to 'false', Faraday will not verify the SSL certificate. This is mostly
38
+ # used for testing. Default is 'true'.
30
39
  def verify_ssl?
31
40
  ENV['SS_CLIENT_SSL'] != 'false'
32
41
  end
33
42
 
43
+ # @return [String] Alphanumeric API key that grants access to the API.
34
44
  def api_key
35
45
  ENV['SS_CLIENT_KEY']
36
46
  end
@@ -7,11 +7,16 @@ module Scholarsphere
7
7
  # Loads the yaml configuration file for the client. The default location is `config/scholarsphere-client.yml` and
8
8
  # the client will load this file automatically whenever it is invoked.
9
9
  #
10
- # The configuration file should contain the endpoint of the Scholarsphere API.
10
+ # The configuration file should contain the endpoint of the Scholarsphere API and the API key.
11
11
  #
12
- # # Example
12
+ # ## Required
13
13
  #
14
- # SS4_ENDPOINT: "http://scholarsphere.psu.edu/api/v1"
14
+ # SS4_ENDPOINT: "https://scholarsphere.psu.edu/api/v1"
15
+ # SS_CLIENT_KEY: "[key]"
16
+ #
17
+ # ## Optional
18
+ #
19
+ # SS_CLIENT_SSL: "false"
15
20
  #
16
21
  class Config
17
22
  # @private
@@ -2,12 +2,98 @@
2
2
 
3
3
  module Scholarsphere
4
4
  module Client
5
+ ##
6
+ #
7
+ # Uploads a complete work into Scholarsphere. If successful, the work will be published and made
8
+ # publicly available.
9
+ #
10
+ # ## Publishing a New Work
11
+ #
12
+ # The most common use case is uploading a single file with the required metadata:
13
+ #
14
+ # ingest = Scholarsphere::Client::Ingest.new(
15
+ # metadata: metadata,
16
+ # files: files,
17
+ # depositor: depositor
18
+ # )
19
+ # response = ingest.publish
20
+ #
21
+ # If the response is successful, the application returns 200 OK with JSON:
22
+ #
23
+ # puts response.body
24
+ # {
25
+ # "message": "Work was successfully created",
26
+ # "url": "/resources/0797e99c-7d4f-4e05-8bf6-86aea1029a6a"
27
+ # }
28
+ #
29
+ # Other possible outcomes include:
30
+ #
31
+ # * work was created, but not successfully published because of missing attributes (201 Created)
32
+ # * work could not be created due to insufficient parameters (422 Unprocessable Entity)
33
+ # * there was an error with the application (500 Internal Server Error)
34
+ #
35
+ # If possible, the response will include additional information about which errors occurred and which attributes are
36
+ # required or are incorrect.
37
+ #
38
+ # ## Metadata
39
+ #
40
+ # A hash of descriptive metadata about the work. The minimal amount required in order to publish would be:
41
+ #
42
+ # {
43
+ # title: "[descriptive title of the work]",
44
+ # creators_attributes: [
45
+ # {
46
+ # display_name: '[Penn State Person]',
47
+ # actor_attributes: {
48
+ # psu_id: 'abc123',
49
+ # surname: '[family name]',
50
+ # given_name: '[given name]',
51
+ # email: 'abc123@psu.edu'
52
+ # }
53
+ # }
54
+ # ]
55
+ # }
56
+ #
57
+ # For a complete listing of all the possible metadata values, see the OpenAPI docs for
58
+ # Scholarsphere.
59
+ #
60
+ # ## Files
61
+ #
62
+ # [
63
+ # Pathname.new('MyPaper.pdf'),
64
+ # Pathname.new('dataset.dat')
65
+ # ]
66
+ #
67
+ # One or more files are required in order for the work be published. The simplest method is an array of `IO`
68
+ # objects. The client then uploads them into S3.
69
+ #
70
+ # *Note: All filenames must have an extension!*
71
+ #
72
+ # ## Depositor
73
+ #
74
+ # {
75
+ # psu_id: '[Penn State Person]',
76
+ # surname: '[family name]',
77
+ # given_name: '[given name]',
78
+ # email: 'abc123@psu.edu'
79
+ # }
80
+ #
81
+ # Currently, the person identified as the depositor must have an active access account at Penn State. In most cases,
82
+ # the depositor will be the same person as the creator specified in the metadata; although, this is not always the
83
+ # case. The depositor may be someone who is uploading on behalf of the creator and is not affiliated with the
84
+ # creation of the work. The depositor is *not* the client itself, either. The client is identified separately via
85
+ # the API token.
86
+ #
87
+ # Note that the fields are the same for both `creators_attributes` and `depositor`, so if the depositor is the same
88
+ # as the creator, all the values will need to be duplicated.
89
+ #
90
+ #
5
91
  class Ingest
6
92
  attr_reader :content, :metadata, :depositor, :permissions
7
93
 
8
94
  # @param metadata [Hash] Metadata attributes
9
95
  # @param files [Array<File,IO,Pathnme>,Hash] An array of File or IO objects, or a hash with a :file param
10
- # @param depositor [String] The access ID of the depositor
96
+ # @param depositor [Hash] The name and access id of the depositor
11
97
  # @param permissions [Hash] (optional) Additional permissions to apply to the resource
12
98
  def initialize(metadata:, files:, depositor:, permissions: {})
13
99
  @content = build_content_hash(files)
@@ -16,6 +102,7 @@ module Scholarsphere
16
102
  @permissions = permissions
17
103
  end
18
104
 
105
+ # @return [Faraday::Response] The response from the Scholarsphere application.
19
106
  def publish
20
107
  upload_files
21
108
  connection.post do |req|
@@ -29,24 +116,20 @@ module Scholarsphere
29
116
  def build_content_hash(files)
30
117
  files.map do |file|
31
118
  if file.is_a?(Hash)
32
- file.merge(file: S3::UploadedFile.new(file.fetch(:file)))
119
+ file.merge(file: S3::UploadedFile.new(source: file.fetch(:file)))
33
120
  else
34
- { file: S3::UploadedFile.new(file) }
121
+ { file: S3::UploadedFile.new(source: file) }
35
122
  end
36
123
  end
37
124
  end
38
125
 
39
126
  def upload_files
40
127
  content.map do |file_parameters|
41
- uploader.upload(file_parameters.fetch(:file))
42
- file_parameters[:file] = file_parameters[:file].to_shrine.to_json
128
+ S3::Uploader.new(file: file_parameters.fetch(:file)).upload
129
+ file_parameters[:file] = file_parameters[:file].to_param.to_json
43
130
  end
44
131
  end
45
132
 
46
- def uploader
47
- @uploader ||= S3::Uploader.new
48
- end
49
-
50
133
  def connection
51
134
  Scholarsphere::Client.connection
52
135
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scholarsphere
4
+ module Client
5
+ ##
6
+ #
7
+ # Requests a pre-signed url, id, and prefix from Scholarsphere for uploading a given file. In order to generate a
8
+ # correct path, the file's extension name is required for the request. The url is used to upload the file's binary
9
+ # content into Scholarsphere, while the id and key are used when adding the file to a work.
10
+ #
11
+ class Upload
12
+ # @param extname [String] Extension of the file to be uploaded, without the period, such as 'pdf'
13
+ def initialize(extname:)
14
+ @extname = extname
15
+ end
16
+
17
+ # @return [String] Prefix where the file is stored in the S3 bucket.
18
+ def prefix
19
+ data['prefix']
20
+ end
21
+
22
+ # @return [String] URL for uploading the file into AWS.
23
+ def url
24
+ data['url']
25
+ end
26
+
27
+ # @return [String] A unique identifier for the file which will serve as its name in S3.
28
+ def id
29
+ data['id']
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :extname
35
+
36
+ def request
37
+ @request ||= Scholarsphere::Client.connection.post do |req|
38
+ req.url 'uploads'
39
+ req.body = { extension: extname }.to_json
40
+ end
41
+ end
42
+
43
+ def data
44
+ @data ||= begin
45
+ raise Client::Error unless request.success?
46
+
47
+ JSON.parse(request.body)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Scholarsphere
4
4
  module Client
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -4,10 +4,10 @@ module Scholarsphere
4
4
  module S3
5
5
  ##
6
6
  #
7
- # Represents a file on the client's file system that will be uploaded, but hasn't been yet. The object is
8
- # constructed using a pathname for the file, and if desired, a checksum. The checksum is not required, and if it is
9
- # not provided, the client will calculate one. Once initialized, the uploaded file object can be used by the client
10
- # to make additional calls to the application.
7
+ # Represents a file on the client's file system to be uploaded into Scholarsphere. The object is constructed using a
8
+ # pathname for the file, and an optional checksum. Once initialized, the object is used by the client to make
9
+ # additional calls to the application, including uploading the file into S3, and adding the resulting uploaded file
10
+ # to a work in Scholarsphere.
11
11
  #
12
12
  # ## Examples
13
13
  #
@@ -16,46 +16,38 @@ module Scholarsphere
16
16
  # pathname = Pathname.new('path/to/your/file')
17
17
  # uploaded_file = UploadedFile.new(pathname)
18
18
  #
19
- # If the file is large, then calculating a checksum could be time-intensive. Providing one can avoid that. Or, if
19
+ # If the file is large, then calculating a checksum could be time-intensive. Providing one can avoid that, or, if
20
20
  # you already have a checksum from a trusted source, you can pass that along for the client to use:
21
21
  #
22
22
  # uploaded_file = UploadedFile.new(pathname, checksum: '[md5 checksum hash]')
23
23
  #
24
+ # ## Checksum Verification
25
+ #
26
+ # Checksums are always used. If you don't provide one, the client will calculate one for you and use it when
27
+ # uploading the file, such as with S3::Uploader. AWS uses the provided checksum to verify the file's integrity, and
28
+ # if that check fails, an exception is raised. This avoids the prospect that the file could be corrupted during its
29
+ # transfer from the local client's filesystem into S3.
30
+ #
24
31
  class UploadedFile
25
- # @return [Pathname] The location of the file to be uploaded
32
+ # @return [Pathname] The file to be uploaded on the client's filesystem
26
33
  attr_reader :source
27
34
 
28
- # @param source [Pathname] The file to be uploaded
29
- # @param options [Hash]
30
- # @option options [String] :checksum The file's md5 checksum. If one is not provided, it will be calculated at
31
- # upload
32
- def initialize(source, options = {})
33
- @source = source
34
- @checksum = options[:checksum]
35
+ # @param source [Pathname, File, IO, String] Object or path to the file
36
+ # @param checksum [String] Optional md5 checksum of the file
37
+ def initialize(source:, checksum: nil)
38
+ @source = Pathname.new(source)
39
+ @checksum = checksum
35
40
  end
36
41
 
37
- # @return [Hash] Set of metadata needed by Shrine to upload the file
38
- # @note this can be passed to a controller for uploading the file to Shrine
39
- def to_shrine
42
+ # @return [Hash] Parameters required to add the file to a work in Scholarsphere
43
+ def to_param
40
44
  {
41
- id: id,
42
- storage: 'cache',
45
+ id: upload.id,
46
+ storage: upload.prefix,
43
47
  metadata: metadata
44
48
  }
45
49
  end
46
50
 
47
- # @return [String] A unique, randomly-generated UUID
48
- # @note This serves as the name of the file in the S3 bucket. However, this original name of the file is kept as
49
- # metadata within the application so that it can be downloaded.
50
- def id
51
- @id ||= "#{SecureRandom.uuid}#{source.extname}"
52
- end
53
-
54
- # @return [String] Path of the file relative to the bucket in S3
55
- def key
56
- "#{prefix}/#{id}"
57
- end
58
-
59
51
  # @return [String] The md5 checksum encoded in base64. If you provided a checksum at initialization, that one will
60
52
  # be encoded, if not, a checksum will be calculated and then encoded.
61
53
  # @note When sending the checksum to verify the file's integrity, Amazon requires that the value be base64
@@ -70,9 +62,9 @@ module Scholarsphere
70
62
  end
71
63
  end
72
64
 
73
- # @return [String] Size of the file in bytes
74
- def size
75
- source.size
65
+ # @return [String] Pre-signed url used to upload the file into Scholarsphere's S3 instance.
66
+ def presigned_url
67
+ upload.url
76
68
  end
77
69
 
78
70
  private
@@ -87,8 +79,8 @@ module Scholarsphere
87
79
  }
88
80
  end
89
81
 
90
- def prefix
91
- ENV['SHRINE_CACHE_PREFIX'] || 'cache'
82
+ def upload
83
+ @upload ||= Client::Upload.new(extname: source.extname)
92
84
  end
93
85
  end
94
86
  end
@@ -2,43 +2,43 @@
2
2
 
3
3
  module Scholarsphere
4
4
  module S3
5
- class Uploader < ::Aws::S3::FileUploader
6
- FIFTEEN_MEGABYTES = 15 * 1024 * 1024
7
-
8
- # @param [Hash] options
9
- # @option options [Client] :client
10
- # @option options [Integer] :multipart_threshold (15728640)
11
- def initialize(options = {})
12
- @options = options
13
- @options[:client] ||= ::Aws::S3::Client.new(client_defaults)
14
- @client = options[:client]
15
- @multipart_threshold = options[:multipart_threshold] || FIFTEEN_MEGABYTES
5
+ ##
6
+ #
7
+ # Uploads a file to Scholarsphere's AWS S3 instance. When upload is invoked, a pre-signed URL is requested from
8
+ # Scholarsphere, and if successful, the file is then uploaded to Scholarsphere using the url.
9
+ #
10
+ # An md5 hash is calculated for the file at initialization to ensure the file is transferred successfully.
11
+ #
12
+ # # Example
13
+ #
14
+ # uploaded_file = Scholarsphere::S3::UploadedFile.new('path/to/file')
15
+ # uploader = Scholarsphere::S3::Uploader(file: uploaded_file)
16
+ # response = uploader.upload
17
+ #
18
+ class Uploader
19
+ # @param file [UploadedFile]
20
+ def initialize(file:)
21
+ @file = file
22
+ @content_md5 = file.content_md5
16
23
  end
17
24
 
18
- # @param [UploadedFile] uploaded_file
19
- # @param [Hash] options of additional options
20
- # @option options [String] content_md5 a base64-encoded string representating the md5 checksum
21
- # @return [void]
22
- # @note The content_md5 hash cannot be used when doing a multipart upload.
23
- def upload(uploaded_file, options = {})
24
- options[:bucket] = ENV['AWS_BUCKET']
25
- options[:key] = uploaded_file.key
26
- if uploaded_file.size < multipart_threshold
27
- options[:content_md5] = uploaded_file.content_md5
25
+ # @return [Faraday::Response] The response from Scholarsphere to the upload request.
26
+ def upload
27
+ connection(file.presigned_url).put do |req|
28
+ req.body = file.source.read
29
+ req.headers['Content-MD5'] = file.content_md5
28
30
  end
29
- super(uploaded_file.source, options)
30
31
  end
31
32
 
32
33
  private
33
34
 
34
- def client_defaults
35
- {
36
- endpoint: ENV['S3_ENDPOINT'],
37
- access_key_id: ENV['AWS_ACCESS_KEY_ID'],
38
- secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
39
- force_path_style: true,
40
- region: ENV['AWS_REGION']
41
- }
35
+ attr_reader :file, :content_md5
36
+
37
+ def connection(url)
38
+ Faraday::Connection.new(
39
+ url: url,
40
+ ssl: { verify: Scholarsphere::Client.verify_ssl? }
41
+ )
42
42
  end
43
43
  end
44
44
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.metadata = {
18
18
  'homepage_uri' => spec.homepage,
19
19
  'source_code_uri' => spec.homepage,
20
- 'documentation' => 'https://www.rubydoc.info/gems/scholarsphere-client',
20
+ 'documentation_uri' => 'https://www.rubydoc.info/github/psu-stewardship/scholarsphere-client/main',
21
21
  'allowed_push_host' => 'https://rubygems.org'
22
22
  }
23
23
 
@@ -41,6 +41,10 @@ Gem::Specification.new do |spec|
41
41
  spec.add_development_dependency 'rake', '>= 12.3.3'
42
42
  spec.add_development_dependency 'rspec', '~> 3.0'
43
43
  spec.add_development_dependency 'rspec-its', '~> 1.3'
44
- spec.add_development_dependency 'simplecov', '~> 0.18'
44
+ spec.add_development_dependency 'vcr', '~> 6.0'
45
+ spec.add_development_dependency 'webmock', '~> 3.11'
45
46
  spec.add_development_dependency 'yard', '< 1.0'
47
+
48
+ # Latest version of simplecov is not compatible with Code Climate
49
+ spec.add_development_dependency 'simplecov', '< 0.18'
46
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scholarsphere-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Wead
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-14 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3
@@ -151,19 +151,33 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: '1.3'
153
153
  - !ruby/object:Gem::Dependency
154
- name: simplecov
154
+ name: vcr
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '0.18'
159
+ version: '6.0'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '0.18'
166
+ version: '6.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: webmock
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '3.11'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '3.11'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: yard
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +192,20 @@ dependencies:
178
192
  - - "<"
179
193
  - !ruby/object:Gem::Version
180
194
  version: '1.0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: simplecov
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "<"
200
+ - !ruby/object:Gem::Version
201
+ version: '0.18'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "<"
207
+ - !ruby/object:Gem::Version
208
+ version: '0.18'
181
209
  description: Client software to create new content for the Scholarsphere repository
182
210
  at Penn State.
183
211
  email:
@@ -186,11 +214,13 @@ executables: []
186
214
  extensions: []
187
215
  extra_rdoc_files: []
188
216
  files:
217
+ - ".circleci/config.yml"
189
218
  - ".gitignore"
190
219
  - ".rspec"
191
220
  - ".rubocop.yml"
192
221
  - ".yardopts"
193
222
  - Gemfile
223
+ - Gemfile.lock
194
224
  - README.md
195
225
  - Rakefile
196
226
  - bin/console
@@ -200,6 +230,7 @@ files:
200
230
  - lib/scholarsphere/client/collection.rb
201
231
  - lib/scholarsphere/client/config.rb
202
232
  - lib/scholarsphere/client/ingest.rb
233
+ - lib/scholarsphere/client/upload.rb
203
234
  - lib/scholarsphere/client/version.rb
204
235
  - lib/scholarsphere/s3.rb
205
236
  - lib/scholarsphere/s3/uploaded_file.rb
@@ -210,7 +241,7 @@ licenses: []
210
241
  metadata:
211
242
  homepage_uri: https://github.com/psu-stewardship/scholarsphere-client
212
243
  source_code_uri: https://github.com/psu-stewardship/scholarsphere-client
213
- documentation: https://www.rubydoc.info/gems/scholarsphere-client
244
+ documentation_uri: https://www.rubydoc.info/github/psu-stewardship/scholarsphere-client/main
214
245
  allowed_push_host: https://rubygems.org
215
246
  post_install_message:
216
247
  rdoc_options: []