imagekitio 1.0.10 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +292 -82
  3. data/Rakefile +1 -1
  4. data/lib/active_storage/active_storage.rb +2 -0
  5. data/lib/active_storage/service/ik_file.rb +111 -0
  6. data/lib/active_storage/service/image_kit_io_service.rb +188 -0
  7. data/lib/carrierwave/carrierwave.rb +79 -0
  8. data/lib/carrierwave/storage/ik_file.rb +8 -7
  9. data/lib/carrierwave/storage/imagekit_store.rb +54 -51
  10. data/lib/carrierwave/support/uri_filename.rb +9 -7
  11. data/lib/imagekitio/api_service/bulk.rb +58 -0
  12. data/lib/imagekitio/api_service/custom_metadata_field.rb +52 -0
  13. data/lib/imagekitio/api_service/file.rb +175 -0
  14. data/lib/imagekitio/api_service/folder.rb +49 -0
  15. data/lib/imagekitio/base.rb +12 -0
  16. data/lib/imagekitio/client.rb +186 -0
  17. data/lib/imagekitio/configurable.rb +43 -0
  18. data/lib/imagekitio/constant.rb +36 -0
  19. data/lib/imagekitio/constants/default.rb +22 -0
  20. data/lib/imagekitio/constants/error.rb +69 -0
  21. data/lib/imagekitio/constants/file.rb +11 -0
  22. data/lib/imagekitio/constants/supported_transformation.rb +64 -0
  23. data/lib/imagekitio/constants/url.rb +14 -0
  24. data/lib/imagekitio/errors.rb +4 -0
  25. data/lib/imagekitio/railtie.rb +1 -1
  26. data/lib/imagekitio/request.rb +91 -0
  27. data/lib/imagekitio/sdk/version.rb +5 -0
  28. data/lib/imagekitio/url.rb +241 -0
  29. data/lib/imagekitio/utils/calculation.rb +44 -0
  30. data/lib/imagekitio/utils/formatter.rb +48 -0
  31. data/lib/imagekitio/utils/option_validator.rb +36 -0
  32. data/lib/imagekitio.rb +10 -83
  33. metadata +55 -15
  34. data/lib/imagekit/constants/defaults.rb +0 -20
  35. data/lib/imagekit/constants/errors.rb +0 -77
  36. data/lib/imagekit/constants/file.rb +0 -5
  37. data/lib/imagekit/constants/supported_transformation.rb +0 -57
  38. data/lib/imagekit/constants/url.rb +0 -9
  39. data/lib/imagekit/file.rb +0 -133
  40. data/lib/imagekit/imagekit.rb +0 -117
  41. data/lib/imagekit/resource.rb +0 -56
  42. data/lib/imagekit/sdk/version.rb +0 -5
  43. data/lib/imagekit/url.rb +0 -237
  44. data/lib/imagekit/utils/calculation.rb +0 -36
  45. data/lib/imagekit/utils/formatter.rb +0 -29
@@ -0,0 +1,188 @@
1
+ require_relative './ik_file'
2
+
3
+ if defined? Rails
4
+ # Overwrite the ActiveStorage::Downloader's open method and remove the file integrity check constraint method verify_integrity_of
5
+ class DownloaderExtension < ::ActiveStorage::Downloader
6
+ def open(key, checksum:, name: "ActiveStorage-", tmpdir: nil, **options)
7
+ open_tempfile(name, tmpdir) do |file|
8
+ download key, file
9
+ # verify_integrity_of file, checksum: checksum
10
+ yield file
11
+ end
12
+ end
13
+ end
14
+
15
+ module ActiveStorageBlobExtension
16
+ def self.included(base)
17
+ base.class_eval do
18
+ before_update :check_metadata
19
+ before_destroy :remove_imagekit_file
20
+ # ActiveRecord::Blob class first destroy the record data and then calls the service.delete method with key
21
+ # as an argument. But the imagekit.io needs fileId to destroy the file which can be get from the metadata.
22
+ # So first destroy the remote file and then destroy the local blob record.
23
+ def remove_imagekit_file
24
+ service.class.delete_ik_file(self)
25
+ end
26
+
27
+ def remote_file_exist?
28
+ service.exist?(self.key)
29
+ end
30
+
31
+ def remote_file
32
+ return false unless remote_file_exist?
33
+
34
+ service.class.remote_file(self)
35
+ end
36
+
37
+ # Needs to reload the record to reflect updated remote meta data.
38
+ def check_metadata
39
+ self.reload
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Rails.application.config.to_prepare do
46
+ ActiveStorage::Blob.send :include, ::ActiveStorageBlobExtension
47
+ end
48
+
49
+ module ActiveStorage
50
+ class Service::ImageKitIoService < Service
51
+ include ImageKitIo::Constantable
52
+
53
+ class << self
54
+ def delete_ik_file(blob)
55
+ ik_file(blob).delete
56
+ end
57
+
58
+ def remote_file(blob)
59
+ ik_file(blob)
60
+ end
61
+
62
+ def ik_file(blob)
63
+ self.new.send(:ik_file).new(blob.metadata)
64
+ end
65
+ end
66
+
67
+ def initialize(**options)
68
+ @options = options
69
+ end
70
+
71
+ def upload(key, io, checksum: nil, **options)
72
+ instrument :upload, key: key, checksum: checksum do
73
+ blob = storage_blob(key)
74
+ response = client.upload_file(file: io, file_name: blob.filename.to_s, content_type: blob.content_type)
75
+ if response[:error].nil?
76
+ blob.update_columns(metadata: response[:response].transform_keys(&:to_sym))
77
+ else
78
+ raise Exception.new response[:error]
79
+ end
80
+ end
81
+ end
82
+
83
+ def download(key, &block)
84
+ if block_given?
85
+ instrument :stream_file, key: key do
86
+ stream_file(key, &block)
87
+ end
88
+ else
89
+ instrument :download, key: key do
90
+ image_kit_file(key)
91
+ end
92
+ end
93
+ end
94
+
95
+ def download_chunk(key, range)
96
+ puts 'Not implemented download_chunk'
97
+ end
98
+
99
+ def delete(key)
100
+ instrument :delete, key: key do
101
+ # image kit file is already deleted on before blob destroy callback
102
+ key
103
+ end
104
+ end
105
+
106
+ def delete_prefixed(prefix)
107
+ # delete the variants files
108
+ puts 'Not implemented delete_prefixed'
109
+ end
110
+
111
+ def exist?(key)
112
+ image_kit_file(key).exist?
113
+ end
114
+
115
+ def url_for_direct_upload(key, **options)
116
+ instrument :url, key: key do |payload|
117
+ options.delete(:content_length)
118
+ options.delete(:checksum)
119
+ url = "#{constants.BASE_URL}#{constants.UPLOAD}"
120
+ generated_url = client.url(src: url)
121
+ payload[:url] = generated_url
122
+ generated_url
123
+ end
124
+ end
125
+
126
+ def headers_for_direct_upload(key, content_type:, checksum:, **options)
127
+ {
128
+ 'Content-Type' => content_type
129
+ }
130
+ end
131
+
132
+ def path_for(key)
133
+ image_kit_file(key).path
134
+ end
135
+
136
+ def url(key, filename: nil, content_type: '', **options)
137
+ generate_url(key, filename: filename, content_type: content_type, path: image_kit_file(key).path, **options)
138
+ end
139
+
140
+ def open(*args, **options, &block)
141
+ DownloaderExtension.new(self).open(*args, **options, &block)
142
+ end
143
+
144
+ private
145
+
146
+ def private_url(key, expires_in:, filename:, disposition:, content_type:, **options)
147
+ generate_url(key, expires_in: expires_in, filename: filename, disposition: disposition, content_type: content_type, path: image_kit_file(key).path, **options)
148
+ end
149
+
150
+ def generate_url(key, expires_in:, filename:, content_type:, disposition:, **options)
151
+ # filename = filename.to_s if filename.is_a? ActiveStorage::Filename
152
+ # options[:filename] = filename if filename
153
+ client.url(url_endpoint: config.url_endpoint, **options)
154
+ end
155
+
156
+ def client
157
+ ImageKitIo.client
158
+ end
159
+
160
+ def config
161
+ ImageKitIo.config
162
+ end
163
+
164
+ def storage_blob(key)
165
+ ActiveStorage::Blob.find_by_key(key)
166
+ end
167
+
168
+ def image_kit_file(key)
169
+ blob = storage_blob(key)
170
+ ik_file.new(blob.metadata)
171
+ end
172
+
173
+ def ik_file
174
+ ImageKiIo::ActiveStorage::IKFile
175
+ end
176
+
177
+ def stream_file(key, &download_block)
178
+ file_obj = image_kit_file(key)
179
+ block = proc { |response|
180
+ response.read_body do |chunk|
181
+ download_block.call(chunk) if download_block.present?
182
+ end
183
+ }
184
+ client.stream_file(file_url: file_obj.url, &block)
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,79 @@
1
+ require 'carrierwave'
2
+ require_relative './storage/imagekit_store'
3
+ require_relative './storage/ik_file'
4
+ require_relative './support/uri_filename'
5
+
6
+ module ImageKitIo
7
+ module CarrierWave
8
+ def self.included(base)
9
+ base.include InstanceMethods
10
+ base.class_eval do
11
+ configure do |config|
12
+ config.storage_engines[:imagekit_store] = 'ImageKitIo::CarrierWave::Storage::ImageKitStore'
13
+ end
14
+ end
15
+ base.storage :imagekit_store
16
+ end
17
+
18
+ module InstanceMethods
19
+ def initialize(*)
20
+ @imagekit = ImageKitIo.client
21
+ @options = {}
22
+ end
23
+
24
+ def filename
25
+ if options != nil
26
+ @options = options
27
+ end
28
+ folder = nil
29
+ begin
30
+ folder = store_dir
31
+ rescue
32
+ end
33
+
34
+ if folder != nil
35
+ @options[:folder] = folder
36
+ end
37
+
38
+ if self.file != nil
39
+ resp = @imagekit.upload_file(file: open(self.file.file, 'rb'), file_name: self.file.filename, **@options)
40
+ # ::File.delete(self.file.file)
41
+ res = resp[:response].to_json
42
+ if res != "null"
43
+ res
44
+ else
45
+ "{\"filePath\":\"\",\"url\":\"\",\"name\":\"\"}"
46
+ end
47
+ else
48
+ "{\"filePath\":\"\",\"url\":\"\",\"name\":\"\"}"
49
+ end
50
+ end
51
+
52
+ def fileId
53
+ JSON.parse(self.identifier)['fileId']
54
+ end
55
+
56
+ def blob
57
+ JSON.parse(self.identifier)
58
+ end
59
+
60
+ def url_with(opt)
61
+ path = JSON.parse(self.identifier)['filePath']
62
+ opt[:path] = path
63
+ url = @imagekit.url(opt)
64
+ end
65
+
66
+ def url
67
+ JSON.parse(self.identifier)['url']
68
+ end
69
+
70
+ def options
71
+ options = {}
72
+ end
73
+
74
+ def store_dir
75
+ store_dir = nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,17 +1,17 @@
1
- module CarrierWave
1
+ module ImageKitIo
2
+ module CarrierWave
2
3
  module Storage
3
4
  class IKFile
4
5
  # Initialize as required.
5
-
6
+
6
7
  def initialize(identifier)
7
8
  @identifier=JSON.parse(identifier)
8
- ik_config=Rails.application.config.imagekit
9
- @imagekit=ImageKit::ImageKitClient.new(ik_config[:private_key],ik_config[:public_key],ik_config[:url_endpoint])
9
+ @imagekit = ImageKitIo.client
10
10
  end
11
11
 
12
- # Duck-type methods for CarrierWave::SanitizedFile.
12
+ # Duck-type methods for CarrierWave::SanitizedFile.
13
13
  def content_type
14
- "image/jpg"
14
+ "image/jpg"
15
15
  end
16
16
  def public_url
17
17
  @identifier['url']
@@ -33,7 +33,7 @@ module CarrierWave
33
33
  def delete
34
34
  # file_id=@identifier['fileId']
35
35
  begin
36
- @imagekit.delete_file(fileId)
36
+ @imagekit.delete_file(file_id: fileId)
37
37
  rescue
38
38
  fileId
39
39
  end
@@ -47,4 +47,5 @@ module CarrierWave
47
47
 
48
48
  end
49
49
 
50
+ end
50
51
  end
@@ -1,65 +1,68 @@
1
1
  require 'uri'
2
2
  require 'net/http'
3
3
  require 'base64'
4
- module CarrierWave
4
+
5
+ module ImageKitIo
6
+ module CarrierWave
5
7
  module Storage
6
- class ImageKitStore < Abstract
8
+ class ImageKitStore < ::CarrierWave::Storage::Abstract
7
9
 
8
- def initialize(*)
9
- super
10
- @cache_called = nil
11
- end
12
-
13
- def store!(file)
14
- file.delete
15
- end
10
+ def initialize(*)
11
+ super
12
+ @cache_called = nil
13
+ end
16
14
 
17
- def retrieve!(identifier)
18
-
19
- IKFile.new(identifier)
20
- end
15
+ def store!(file)
16
+ file.delete
17
+ end
21
18
 
22
- def cache!(new_file)
23
- new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
24
- rescue Errno::EMLINK, Errno::ENOSPC => e
25
- raise(e) if @cache_called
26
- @cache_called = true
27
-
28
- # NOTE: Remove cached files older than 10 minutes
29
- clean_cache!(600)
30
-
31
- cache!(new_file)
32
- end
19
+ def retrieve!(identifier)
33
20
 
34
- def retrieve_from_cache!(identifier)
35
- CarrierWave::SanitizedFile.new(::File.expand_path(uploader.cache_path(identifier), uploader.root))
36
- end
21
+ IKFile.new(identifier)
22
+ end
37
23
 
38
- def delete_dir!(path)
39
- if path
40
- begin
41
- Dir.rmdir(::File.expand_path(path, uploader.root))
42
- rescue Errno::ENOENT
43
- # Ignore: path does not exist
44
- rescue Errno::ENOTDIR
45
- # Ignore: path is not a dir
46
- rescue Errno::ENOTEMPTY, Errno::EEXIST
47
- # Ignore: dir is not empty
48
- end
49
- end
24
+ def cache!(new_file)
25
+ new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
26
+ rescue Errno::EMLINK, Errno::ENOSPC => e
27
+ raise(e) if @cache_called
28
+ @cache_called = true
29
+
30
+ # NOTE: Remove cached files older than 10 minutes
31
+ clean_cache!(600)
32
+
33
+ cache!(new_file)
34
+ end
35
+
36
+ def retrieve_from_cache!(identifier)
37
+ CarrierWave::SanitizedFile.new(::File.expand_path(uploader.cache_path(identifier), uploader.root))
38
+ end
39
+
40
+ def delete_dir!(path)
41
+ if path
42
+ begin
43
+ Dir.rmdir(::File.expand_path(path, uploader.root))
44
+ rescue Errno::ENOENT
45
+ # Ignore: path does not exist
46
+ rescue Errno::ENOTDIR
47
+ # Ignore: path is not a dir
48
+ rescue Errno::ENOTEMPTY, Errno::EEXIST
49
+ # Ignore: dir is not empty
50
+ end
50
51
  end
52
+ end
51
53
 
52
- def clean_cache!(seconds)
53
- Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
54
- # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
55
- time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
56
- time = Time.at(*time)
57
- if time < (Time.now.utc - seconds)
58
- FileUtils.rm_rf(dir)
59
- end
60
- end
54
+ def clean_cache!(seconds)
55
+ Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
56
+ # generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
57
+ time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
58
+ time = Time.at(*time)
59
+ if time < (Time.now.utc - seconds)
60
+ FileUtils.rm_rf(dir)
61
+ end
61
62
  end
62
-
63
63
  end
64
+
65
+ end
64
66
  end
65
- end
67
+ end
68
+ end
@@ -1,10 +1,12 @@
1
- module CarrierWave
1
+ module ImageKitIo
2
+ module CarrierWave
2
3
  module Support
3
- module UriFilename
4
- def self.filename(url)
5
- path = url.split('?').first
6
- URI.decode(path).gsub(%r{.*/(.*?$)}, '\1')
7
- end
4
+ module UriFilename
5
+ def self.filename(url)
6
+ path = url.split('?').first
7
+ URI.decode(path).gsub(%r{.*/(.*?$)}, '\1')
8
8
  end
9
+ end
9
10
  end
10
- end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../constant'
2
+
3
+ module ImageKitIo
4
+ module ApiService
5
+ class Bulk
6
+ include Constantable
7
+
8
+ def initialize(req_obj)
9
+ @req_obj = req_obj
10
+ end
11
+
12
+ def job_status(job_id: nil)
13
+ if job_id == '' || job_id.nil?
14
+ raise ArgumentError, 'job_id is required'
15
+ end
16
+ url = "#{constants.BULK_BASE_URL}/#{job_id}"
17
+ payload = { 'jobId': job_id }
18
+ @req_obj.request('get', url, @req_obj.create_headers, payload)
19
+ end
20
+
21
+ def add_tags(file_ids: [], tags: [])
22
+ if file_ids.empty? || tags.empty?
23
+ raise ArgumentError, 'Parameters are required'
24
+ end
25
+ url = "#{constants.BASE_URL}/addTags"
26
+ payload = { 'fileIds': file_ids, 'tags': tags }.to_json
27
+ @req_obj.request('post', url, @req_obj.create_headers, payload)
28
+ end
29
+
30
+ def remove_tags(file_ids: [], tags: [])
31
+ if file_ids.empty? || tags.empty?
32
+ raise ArgumentError, 'Parameters are required'
33
+ end
34
+ url = "#{constants.BASE_URL}/removeTags"
35
+ payload = { 'fileIds': file_ids, 'tags': tags }.to_json
36
+ @req_obj.request('post', url, @req_obj.create_headers, payload)
37
+ end
38
+
39
+ def remove_ai_tags(file_ids: [], ai_tags: [])
40
+ if file_ids.empty? || ai_tags.empty?
41
+ raise ArgumentError, 'Parameters are required'
42
+ end
43
+ url = "#{constants.BASE_URL}/removeAITags"
44
+ payload = { 'fileIds': file_ids, 'AITags': ai_tags }.to_json
45
+ @req_obj.request('post', url, @req_obj.create_headers, payload)
46
+ end
47
+
48
+ def remove_files(file_ids: [])
49
+ if file_ids.empty?
50
+ raise ArgumentError, 'File ids are required'
51
+ end
52
+ url = "#{constants.BASE_URL}#{constants.BULK_FILE_DELETE}"
53
+ payload = {'fileIds': file_ids}.to_json
54
+ @req_obj.request("post", url, @req_obj.create_headers, payload)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../utils/option_validator'
2
+ require_relative '../constant'
3
+
4
+ module ImageKitIo
5
+ module ApiService
6
+ class CustomMetadataField
7
+ include Utils::OptionValidator
8
+ include Constantable
9
+
10
+ def initialize(req_obj)
11
+ @req_obj = req_obj
12
+ end
13
+
14
+ def create(name: nil, label: nil, schema: nil)
15
+ if name == '' || name.nil? || label == '' || label.nil? || schema == '' || schema.nil?
16
+ raise ArgumentError, 'Parameters required'
17
+ end
18
+ unless schema.is_a?(Hash)
19
+ raise ArgumentError, 'Schema must be hash object'
20
+ end
21
+ url = "#{constants.API_BASE_URL}/customMetadataFields"
22
+ payload = { 'name': name, 'label': label, 'schema': schema }.to_json
23
+ @req_obj.request('post', url, @req_obj.create_headers, payload)
24
+ end
25
+
26
+ def list(**options)
27
+ url = "#{constants.API_BASE_URL}/customMetadataFields"
28
+ payload = request_formatter(options || {})
29
+ @req_obj.request('get', url, @req_obj.create_headers, payload)
30
+ end
31
+
32
+ def update(id: nil, label: nil, schema: nil)
33
+ if id == '' || id.nil?
34
+ raise ArgumentError, 'id is required'
35
+ end
36
+ url = "#{constants.API_BASE_URL}/customMetadataFields/#{id}"
37
+ payload = {}
38
+ payload = payload.merge({ 'label': label }) unless label.nil?
39
+ payload = payload.merge({ 'schema': label }) unless schema.nil?
40
+ @req_obj.request('patch', url, @req_obj.create_headers, payload.to_json)
41
+ end
42
+
43
+ def delete(id: nil)
44
+ if id == '' || id.nil?
45
+ raise ArgumentError, 'id is required'
46
+ end
47
+ url = "#{constants.API_BASE_URL}/customMetadataFields/#{id}"
48
+ @req_obj.request('delete', url, @req_obj.create_headers)
49
+ end
50
+ end
51
+ end
52
+ end