paperclip-azure-storage 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/publish.yml +29 -0
- data/.github/workflows/review.yml +23 -0
- data/.gitignore +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +104 -0
- data/LICENSE.md +21 -0
- data/README.md +33 -0
- data/lib/paperclip/storage/azure_storage.rb +112 -0
- data/lib/paperclip-azure-storage.rb +1 -0
- data/paperclip-azure-storage.gemspec +26 -0
- data/spec/fixtures/list_blobs.xml +58 -0
- data/spec/lib/paperclip/storage/azure_storage_spec.rb +135 -0
- data/spec/spec_helper.rb +12 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c343cc5f38fe90088c4a79456704487216ff247a96ab662bb89746a0ce0b998f
|
4
|
+
data.tar.gz: fe1ef1ba0d3c222a3b9d39efc47f5c71f0694154a475abe144953581da1152f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd844d3e4a077ae7120a58edc7432ff5bd09d3ce668ccd2020847af559c43686dd8fb5a3e71ed090caf8b822fef4abd6ca063450beea64abc14c8abb257978fe
|
7
|
+
data.tar.gz: ecbc10ff245e0df92acc55fcd56428c1b22904919fbcb0627ac26e68050cf8ef380daa70e830503e7326c0e2bf071538b4c3c785cca9410d429dde33b4c8ab1e
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags: [ 'v*' ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
packages: write
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v2
|
16
|
+
- name: Set up Ruby 2.5
|
17
|
+
uses: actions/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: 2.5
|
20
|
+
- name: Publish
|
21
|
+
run: |
|
22
|
+
mkdir -p $HOME/.gem
|
23
|
+
touch $HOME/.gem/credentials
|
24
|
+
chmod 0600 $HOME/.gem/credentials
|
25
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
26
|
+
gem build *.gemspec
|
27
|
+
gem push *.gem
|
28
|
+
env:
|
29
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
name: Review
|
12
|
+
runs-on: ubuntu-20.04
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby 2.5
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.5
|
19
|
+
- name: Run rspec
|
20
|
+
run: |
|
21
|
+
gem install bundler --version=1.17.3
|
22
|
+
bundle install
|
23
|
+
rspec
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
paperclip-azure-storage
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.5.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
paperclip-azure-storage (0.1.5)
|
5
|
+
azure-storage-blob (>= 1.0.0, < 3.0.0)
|
6
|
+
paperclip (>= 5.1.0, < 7.0.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (6.1.4.1)
|
12
|
+
activesupport (= 6.1.4.1)
|
13
|
+
activerecord (6.1.4.1)
|
14
|
+
activemodel (= 6.1.4.1)
|
15
|
+
activesupport (= 6.1.4.1)
|
16
|
+
activesupport (6.1.4.1)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (>= 1.6, < 2)
|
19
|
+
minitest (>= 5.1)
|
20
|
+
tzinfo (~> 2.0)
|
21
|
+
zeitwerk (~> 2.3)
|
22
|
+
addressable (2.8.0)
|
23
|
+
public_suffix (>= 2.0.2, < 5.0)
|
24
|
+
azure-core (0.1.15)
|
25
|
+
faraday (~> 0.9)
|
26
|
+
faraday_middleware (~> 0.10)
|
27
|
+
nokogiri (~> 1.6)
|
28
|
+
azure-storage-blob (1.1.0)
|
29
|
+
azure-core (~> 0.1.13)
|
30
|
+
azure-storage-common (~> 1.0)
|
31
|
+
nokogiri (~> 1.6, >= 1.6.8)
|
32
|
+
azure-storage-common (1.1.0)
|
33
|
+
azure-core (~> 0.1.13)
|
34
|
+
nokogiri (~> 1.6, >= 1.6.8)
|
35
|
+
climate_control (0.2.0)
|
36
|
+
concurrent-ruby (1.1.9)
|
37
|
+
crack (0.4.5)
|
38
|
+
rexml
|
39
|
+
diff-lcs (1.4.4)
|
40
|
+
faraday (0.17.4)
|
41
|
+
multipart-post (>= 1.2, < 3)
|
42
|
+
faraday_middleware (0.14.0)
|
43
|
+
faraday (>= 0.7.4, < 1.0)
|
44
|
+
hashdiff (1.0.1)
|
45
|
+
i18n (1.8.10)
|
46
|
+
concurrent-ruby (~> 1.0)
|
47
|
+
mime-types (3.3.1)
|
48
|
+
mime-types-data (~> 3.2015)
|
49
|
+
mime-types-data (3.2021.0901)
|
50
|
+
mimemagic (0.3.10)
|
51
|
+
nokogiri (~> 1)
|
52
|
+
rake
|
53
|
+
mini_portile2 (2.6.1)
|
54
|
+
minitest (5.14.4)
|
55
|
+
multipart-post (2.1.1)
|
56
|
+
nokogiri (1.12.5)
|
57
|
+
mini_portile2 (~> 2.6.1)
|
58
|
+
racc (~> 1.4)
|
59
|
+
paperclip (6.1.0)
|
60
|
+
activemodel (>= 4.2.0)
|
61
|
+
activesupport (>= 4.2.0)
|
62
|
+
mime-types
|
63
|
+
mimemagic (~> 0.3.0)
|
64
|
+
terrapin (~> 0.6.0)
|
65
|
+
public_suffix (4.0.6)
|
66
|
+
racc (1.6.0)
|
67
|
+
rake (13.0.6)
|
68
|
+
rexml (3.2.5)
|
69
|
+
rspec (3.10.0)
|
70
|
+
rspec-core (~> 3.10.0)
|
71
|
+
rspec-expectations (~> 3.10.0)
|
72
|
+
rspec-mocks (~> 3.10.0)
|
73
|
+
rspec-core (3.10.1)
|
74
|
+
rspec-support (~> 3.10.0)
|
75
|
+
rspec-expectations (3.10.1)
|
76
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
77
|
+
rspec-support (~> 3.10.0)
|
78
|
+
rspec-mocks (3.10.2)
|
79
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
+
rspec-support (~> 3.10.0)
|
81
|
+
rspec-support (3.10.2)
|
82
|
+
sqlite3 (1.4.2)
|
83
|
+
terrapin (0.6.0)
|
84
|
+
climate_control (>= 0.0.3, < 1.0)
|
85
|
+
tzinfo (2.0.4)
|
86
|
+
concurrent-ruby (~> 1.0)
|
87
|
+
webmock (3.14.0)
|
88
|
+
addressable (>= 2.8.0)
|
89
|
+
crack (>= 0.3.2)
|
90
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
91
|
+
zeitwerk (2.5.1)
|
92
|
+
|
93
|
+
PLATFORMS
|
94
|
+
ruby
|
95
|
+
|
96
|
+
DEPENDENCIES
|
97
|
+
activerecord (~> 6.1)
|
98
|
+
paperclip-azure-storage!
|
99
|
+
rspec (~> 3.10)
|
100
|
+
sqlite3 (~> 1.4)
|
101
|
+
webmock (~> 3.14)
|
102
|
+
|
103
|
+
BUNDLED WITH
|
104
|
+
1.17.3
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Irsyad Rizaldi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Paperclip Azure Storage
|
2
|
+
## Description
|
3
|
+
`paperclip-azure-storage` is a [paperclip](https://github.com/thoughtbot/paperclip) adapter for azure storage.
|
4
|
+
|
5
|
+
The difference between this and other azure adapter, such as [paperclip-azure](https://github.com/supportify/paperclip-azure),
|
6
|
+
was the ability to use [service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals)
|
7
|
+
as the authentication method.
|
8
|
+
|
9
|
+
## Getting Started
|
10
|
+
1. Add `paperclip-azure-storage` on `Gemfile`
|
11
|
+
```ruby
|
12
|
+
gem 'paperclip-azure-storage'
|
13
|
+
```
|
14
|
+
2. Configure `paperclip`
|
15
|
+
```ruby
|
16
|
+
require 'paperclip-azure-storage'
|
17
|
+
|
18
|
+
Paperclip::DataUriAdapter.register
|
19
|
+
|
20
|
+
Paperclip::Attachment.default_options[:storage] = :azure_storage
|
21
|
+
Paperclip::Attachment.default_options[:tenant_id] = 'c881ddef-bf33-4574-a43b-1876a94c940a'
|
22
|
+
Paperclip::Attachment.default_options[:client_id] = 'e35cf4da-b3cd-4722-9923-deed0bf41a37'
|
23
|
+
Paperclip::Attachment.default_options[:client_secret] = 'J@NcRfUjXn2r5u7x!A%D*G-KaPdSgVkY'
|
24
|
+
Paperclip::Attachment.default_options[:resource] = 'https://mystorage.blob.core.windows.net'
|
25
|
+
Paperclip::Attachment.default_options[:storage_name] = 'mystorage'
|
26
|
+
Paperclip::Attachment.default_options[:container] = 'mycontainer'
|
27
|
+
```
|
28
|
+
3. Upload & access your image
|
29
|
+
```ruby
|
30
|
+
image_content = '<some-image>'
|
31
|
+
model = model_class.create!(image: image_content) # upload image to azure
|
32
|
+
model.image.azure_url # get azure image url
|
33
|
+
```
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Storage
|
3
|
+
module AzureStorage
|
4
|
+
def self.extended(_base)
|
5
|
+
require 'azure/storage/blob'
|
6
|
+
require 'azure/storage/common'
|
7
|
+
end
|
8
|
+
|
9
|
+
def exists?(style_name = default_style)
|
10
|
+
container = @options[:container]
|
11
|
+
storage_client.list_blobs(
|
12
|
+
container,
|
13
|
+
prefix: path(style_name),
|
14
|
+
max_results: 1,
|
15
|
+
timeout: 60
|
16
|
+
).present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def flush_writes
|
20
|
+
container = @options[:container]
|
21
|
+
@queued_for_write.each do |style_name, file|
|
22
|
+
storage_client.create_block_blob(
|
23
|
+
container,
|
24
|
+
path(style_name),
|
25
|
+
file.read,
|
26
|
+
timeout: 60,
|
27
|
+
content_type: file.content_type,
|
28
|
+
content_length: file.size
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
after_flush_writes
|
33
|
+
@queued_for_write = {}
|
34
|
+
end
|
35
|
+
|
36
|
+
def flush_deletes
|
37
|
+
container = @options[:container]
|
38
|
+
@queued_for_delete.each { |path| storage_client.delete_blob(container, path, timeout: 60) }
|
39
|
+
|
40
|
+
@queued_for_delete = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_to_local_file(style, local_dest_path)
|
44
|
+
container = @options[:container]
|
45
|
+
_blob, content = storage_client.get_blob(container, path(style), timeout: 60)
|
46
|
+
::File.open(local_dest_path, 'wb') { |local_file| local_file.write(content) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def azure_url(style_name = default_style)
|
50
|
+
"#{@options[:resource]}/#{@options[:container]}/#{path(style_name)}".squeeze('/')
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def storage_client
|
56
|
+
renew_expired_token
|
57
|
+
@storage_client ||= create_storage_client
|
58
|
+
end
|
59
|
+
|
60
|
+
def renew_expired_token
|
61
|
+
expired = @expires_on.present? && Time.now.to_i >= @expires_on.to_i
|
62
|
+
return unless expired
|
63
|
+
|
64
|
+
@access_token, @expires_on = create_access_token
|
65
|
+
@token_credential.renew_token(@access_token)
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_storage_client
|
69
|
+
storage_name = @options[:storage_name]
|
70
|
+
@access_token, @expires_on = create_access_token
|
71
|
+
@token_credential = ::Azure::Storage::Common::Core::TokenCredential.new(@access_token)
|
72
|
+
token_signer = ::Azure::Storage::Common::Core::Auth::TokenSigner.new(@token_credential)
|
73
|
+
::Azure::Storage::Blob::BlobService.new(storage_account_name: storage_name, signer: token_signer)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_access_token
|
77
|
+
tenant_id = @options[:tenant_id]
|
78
|
+
client_id = @options[:client_id]
|
79
|
+
client_secret = @options[:client_secret]
|
80
|
+
resource = @options[:resource]
|
81
|
+
grant_type = 'client_credentials'
|
82
|
+
|
83
|
+
requested_at = Time.now.to_i
|
84
|
+
response = faraday_client.post("#{tenant_id}/oauth2/token") do |request|
|
85
|
+
request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
86
|
+
request.headers['Accept'] = 'application/json'
|
87
|
+
request_payload = {
|
88
|
+
client_id: client_id,
|
89
|
+
client_secret: client_secret,
|
90
|
+
resource: resource,
|
91
|
+
grant_type: grant_type
|
92
|
+
}
|
93
|
+
request.body = URI.encode_www_form(request_payload)
|
94
|
+
end
|
95
|
+
|
96
|
+
access_token = response.body['access_token']
|
97
|
+
expires_on = requested_at + response.body['expires_in'].to_i
|
98
|
+
|
99
|
+
[access_token, expires_on]
|
100
|
+
end
|
101
|
+
|
102
|
+
def faraday_client
|
103
|
+
@faraday_client ||= Faraday.new('https://login.microsoftonline.com') do |client|
|
104
|
+
client.request :retry
|
105
|
+
client.response :json
|
106
|
+
client.response :raise_error
|
107
|
+
client.adapter :net_http
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'paperclip/storage/azure_storage'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'paperclip-azure-storage'
|
3
|
+
s.version = '0.1.5'
|
4
|
+
s.licenses = ['MIT']
|
5
|
+
s.summary = "Paperclip Adapter for Azure Storage"
|
6
|
+
s.description = 'paperclip-azure-storage is a paperclip adapter for azure storage that use service principal authentication'
|
7
|
+
s.authors = ["Irsyad Rizaldi"]
|
8
|
+
s.email = 'irsyad.rizaldi97@gmail.com'
|
9
|
+
s.homepage = 'https://github.com/dadangeuy/paperclip-azure-storage'
|
10
|
+
s.metadata = { "source_code_uri" => "https://github.com/dadangeuy/paperclip-azure-storage" }
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
14
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
s.require_paths = ['lib']
|
16
|
+
|
17
|
+
s.required_ruby_version = ">= 2.5.0"
|
18
|
+
|
19
|
+
s.add_dependency 'azure-storage-blob', '>= 1.0.0', '< 3.0.0'
|
20
|
+
s.add_dependency 'paperclip', '>= 5.1.0', '< 7.0.0'
|
21
|
+
|
22
|
+
s.add_development_dependency 'activerecord', '~> 6.1'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.10'
|
24
|
+
s.add_development_dependency 'sqlite3', '~>1.4'
|
25
|
+
s.add_development_dependency 'webmock', '~> 3.14'
|
26
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<EnumerationResults ContainerName="https://myaccount.blob.core.windows.net/mycontainer">
|
3
|
+
<MaxResults>4</MaxResults>
|
4
|
+
<Blobs>
|
5
|
+
<Blob>
|
6
|
+
<Name>blob1.txt</Name>
|
7
|
+
<Url>https://myaccount.blob.core.windows.net/mycontainer/blob1.txt</Url>
|
8
|
+
<Properties>
|
9
|
+
<Last-Modified>Sun, 27 Sep 2009 18:41:57 GMT</Last-Modified>
|
10
|
+
<Etag>0x8CAE7D55D050B8B</Etag>
|
11
|
+
<Content-Length>8</Content-Length>
|
12
|
+
<Content-Type>text/html</Content-Type>
|
13
|
+
<Content-Encoding/>
|
14
|
+
<Content-Language>en-US</Content-Language>
|
15
|
+
<Content-MD5/>
|
16
|
+
<Cache-Control>no-cache</Cache-Control>
|
17
|
+
<BlobType>BlockBlob</BlobType>
|
18
|
+
<LeaseStatus>unlocked</LeaseStatus>
|
19
|
+
</Properties>
|
20
|
+
</Blob>
|
21
|
+
<Blob>
|
22
|
+
<Name>blob2.txt</Name>
|
23
|
+
<Url>https://myaccount.blob.core.windows.net/mycontainer/blob2.txt</Url>
|
24
|
+
<Properties>
|
25
|
+
<Last-Modified>Sun, 27 Sep 2009 12:18:50 GMT</Last-Modified>
|
26
|
+
<Etag>0x8CAE7D55CF6C339</Etag>
|
27
|
+
<Content-Length>100</Content-Length>
|
28
|
+
<Content-Type>text/html</Content-Type>
|
29
|
+
<Content-Encoding/>
|
30
|
+
<Content-Language>en-US</Content-Language>
|
31
|
+
<Content-MD5/>
|
32
|
+
<Cache-Control>no-cache</Cache-Control>
|
33
|
+
<BlobType>BlockBlob</BlobType>
|
34
|
+
<LeaseStatus>unlocked</LeaseStatus>
|
35
|
+
</Properties>
|
36
|
+
</Blob>
|
37
|
+
<BlobPrefix>
|
38
|
+
<Name>myfolder/</Name>
|
39
|
+
</BlobPrefix>
|
40
|
+
<Blob>
|
41
|
+
<Name>newblob1.txt</Name>
|
42
|
+
<Url>https://myaccount.blob.core.windows.net/mycontainer/newblob1.txt</Url>
|
43
|
+
<Properties>
|
44
|
+
<Last-Modified>Sun, 27 Sep 2009 16:31:57 GMT</Last-Modified>
|
45
|
+
<Etag>0x8CAE7D55CF6C339</Etag>
|
46
|
+
<Content-Length>25</Content-Length>
|
47
|
+
<Content-Type>text/html</Content-Type>
|
48
|
+
<Content-Encoding/>
|
49
|
+
<Content-Language>en-US</Content-Language>
|
50
|
+
<Content-MD5/>
|
51
|
+
<Cache-Control>no-cache</Cache-Control>
|
52
|
+
<BlobType>BlockBlob</BlobType>
|
53
|
+
<LeaseStatus>unlocked</LeaseStatus>
|
54
|
+
</Properties>
|
55
|
+
</Blob>
|
56
|
+
</Blobs>
|
57
|
+
<NextMarker>newblob2.txt</NextMarker>
|
58
|
+
</EnumerationResults>
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
class MockPaperclip < ActiveRecord::Base
|
4
|
+
include Paperclip::Glue
|
5
|
+
|
6
|
+
has_attached_file(
|
7
|
+
:image,
|
8
|
+
path: 'img/missions/:reversed_path/:style/:filename',
|
9
|
+
url: 'img/missions/:reversed_path/:style/:filename',
|
10
|
+
storage: :azure_storage,
|
11
|
+
tenant_id: 'tenant-id',
|
12
|
+
client_id: 'client-id',
|
13
|
+
resource: 'resource',
|
14
|
+
storage_name: 'storage-name',
|
15
|
+
container: 'container'
|
16
|
+
)
|
17
|
+
validates_attachment_content_type :image, content_type: /\Aimage/
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'AzureStorage' do
|
21
|
+
before(:all, 'create mock table') do
|
22
|
+
MockPaperclip.connection.create_table(MockPaperclip.table_name, force: true) do |table|
|
23
|
+
table.column :image_content_type, :string
|
24
|
+
table.column :image_file_name, :string
|
25
|
+
table.column :image_file_size, :integer
|
26
|
+
table.column :image_updated_at, :datetime
|
27
|
+
table.column :image_fingerprint, :string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
before 'mock login' do
|
32
|
+
method = :post
|
33
|
+
uri = %r{https://login.microsoftonline.com/.+/oauth2/token}
|
34
|
+
status = 200
|
35
|
+
body = {
|
36
|
+
token_type: 'Bearer',
|
37
|
+
expires_in: '3599',
|
38
|
+
ext_expires_in: '3599',
|
39
|
+
expires_on: '1634195094',
|
40
|
+
not_before: '1634191194',
|
41
|
+
resource: 'resource',
|
42
|
+
access_token: 'access_token'
|
43
|
+
}.to_json
|
44
|
+
headers = { 'Content-Type': 'application/json' }
|
45
|
+
|
46
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
47
|
+
end
|
48
|
+
|
49
|
+
before 'mock create_block_blob' do
|
50
|
+
method = :put
|
51
|
+
uri = %r{https://[a-z-]+.blob.core.windows.net/container/.+}
|
52
|
+
status = 201
|
53
|
+
body = ''
|
54
|
+
headers = {}
|
55
|
+
|
56
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
57
|
+
end
|
58
|
+
|
59
|
+
before 'mock list_blobs' do
|
60
|
+
method = :get
|
61
|
+
uri = %r{https://[a-z-]+.blob.core.windows.net/container\?}
|
62
|
+
status = 200
|
63
|
+
body = File.read('./spec/fixtures/list_blobs.xml')
|
64
|
+
|
65
|
+
headers = {}
|
66
|
+
|
67
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
68
|
+
end
|
69
|
+
|
70
|
+
before 'mock get_blob' do
|
71
|
+
method = :get
|
72
|
+
uri = %r{https://storage-name.blob.core.windows.net/container/.+}
|
73
|
+
status = 200
|
74
|
+
body = image
|
75
|
+
headers = {}
|
76
|
+
|
77
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
78
|
+
end
|
79
|
+
|
80
|
+
before 'mock delete_blob' do
|
81
|
+
method = :delete
|
82
|
+
uri = %r{https://[a-z-]+.blob.core.windows.net/container/.+}
|
83
|
+
status = 202
|
84
|
+
body = ''
|
85
|
+
headers = {}
|
86
|
+
|
87
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
88
|
+
end
|
89
|
+
|
90
|
+
let(:image) { 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' }
|
91
|
+
|
92
|
+
describe 'upload image' do
|
93
|
+
subject { MockPaperclip.create(image: image) }
|
94
|
+
|
95
|
+
it 'success' do
|
96
|
+
expect { subject }.not_to raise_error
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '500 response' do
|
100
|
+
before 'mock create_block_blob' do
|
101
|
+
method = :put
|
102
|
+
uri = %r{https://[a-z-]+.blob.core.windows.net/container/.+}
|
103
|
+
status = 500
|
104
|
+
body = ''
|
105
|
+
headers = {}
|
106
|
+
|
107
|
+
stub_request(method, uri).to_return(status: status, body: body, headers: headers)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'raise error' do
|
111
|
+
expect { subject }.to raise_error(Azure::Core::Http::HTTPError)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'delete image' do
|
117
|
+
let(:model) { MockPaperclip.create(image: image) }
|
118
|
+
|
119
|
+
subject { model.image.destroy }
|
120
|
+
|
121
|
+
it 'success' do
|
122
|
+
expect { subject }.not_to raise_error
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'download image' do
|
127
|
+
let(:model) { MockPaperclip.create(image: image) }
|
128
|
+
|
129
|
+
subject { model.image.copy_to_local_file('original', "/tmp/#{SecureRandom.hex(4)}") }
|
130
|
+
|
131
|
+
it 'success' do
|
132
|
+
expect { subject }.not_to raise_error
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require './lib/paperclip/storage/azure_storage'
|
2
|
+
require 'rspec'
|
3
|
+
require 'paperclip'
|
4
|
+
require 'active_record'
|
5
|
+
require 'webmock/rspec'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
adapter: 'sqlite3',
|
9
|
+
database: ':memory:'
|
10
|
+
)
|
11
|
+
|
12
|
+
Paperclip::DataUriAdapter.register
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paperclip-azure-storage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Irsyad Rizaldi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: azure-storage-blob
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: paperclip
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 5.1.0
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 7.0.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 5.1.0
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 7.0.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: activerecord
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '6.1'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '6.1'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rspec
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '3.10'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '3.10'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: sqlite3
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '1.4'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '1.4'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: webmock
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '3.14'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '3.14'
|
109
|
+
description: paperclip-azure-storage is a paperclip adapter for azure storage that
|
110
|
+
use service principal authentication
|
111
|
+
email: irsyad.rizaldi97@gmail.com
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files: []
|
115
|
+
files:
|
116
|
+
- ".github/workflows/publish.yml"
|
117
|
+
- ".github/workflows/review.yml"
|
118
|
+
- ".gitignore"
|
119
|
+
- ".ruby-gemset"
|
120
|
+
- ".ruby-version"
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.md
|
124
|
+
- README.md
|
125
|
+
- lib/paperclip-azure-storage.rb
|
126
|
+
- lib/paperclip/storage/azure_storage.rb
|
127
|
+
- paperclip-azure-storage.gemspec
|
128
|
+
- spec/fixtures/list_blobs.xml
|
129
|
+
- spec/lib/paperclip/storage/azure_storage_spec.rb
|
130
|
+
- spec/spec_helper.rb
|
131
|
+
homepage: https://github.com/dadangeuy/paperclip-azure-storage
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
source_code_uri: https://github.com/dadangeuy/paperclip-azure-storage
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 2.5.0
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.7.6.3
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Paperclip Adapter for Azure Storage
|
156
|
+
test_files: []
|