imagekitio 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ $VERBOSE = nil
4
+
5
+ require_relative "./resource"
6
+ require_relative "./file"
7
+ require_relative "./url"
8
+ require_relative "./utils/calculation"
9
+
10
+ module ImageKit
11
+ class Error < StandardError
12
+ end
13
+
14
+ # ImageKit class holds each method will be used by user
15
+ class ImageKitClient
16
+ attr_reader :file
17
+
18
+ def initialize(private_key, public_key, url_endpoint, transformation_pos = nil, options = nil)
19
+ @private_key = private_key
20
+ @public_key = public_key
21
+ @url_endpoint = url_endpoint
22
+ @transformation_position = transformation_pos
23
+ @options = options
24
+
25
+ @ik_req = ImageKitRequest.new(private_key, public_key, url_endpoint)
26
+ @file = ImageKitFile.new(@ik_req)
27
+ @url_obj = Url.new(@ik_req)
28
+
29
+ end
30
+
31
+ def set_ik_request(ik_req)
32
+ # setter for imagekit request mainly will be used for
33
+ # test
34
+ @ik_req = ik_req
35
+ end
36
+
37
+ def url(options)
38
+ @url_obj.generate_url(options)
39
+ end
40
+
41
+ def upload_file(file = nil, file_name = nil, options = nil)
42
+ # upload file to imagekit server
43
+ @file.upload(file, file_name, options)
44
+ end
45
+
46
+ def list_files(options)
47
+ # list all files
48
+ @file.list(options)
49
+ end
50
+
51
+ def get_file_details(file_identifier)
52
+ # Get file detail by file-id or file_url
53
+ @file.details(file_identifier)
54
+ end
55
+
56
+ def update_file_details(file_id, options)
57
+ # update file details by file id and other options payload
58
+ @file.update_details(file_id, options)
59
+ end
60
+
61
+ def delete_file(file_id)
62
+ # Delete a file by file-id
63
+ @file.delete(file_id)
64
+ end
65
+
66
+ def bulk_file_delete(file_ids)
67
+ # Delete file in bulks by list of file id
68
+ @file.batch_delete(file_ids)
69
+ end
70
+
71
+ def get_file_metadata(file_id)
72
+ # Get metadata of a file by file-id
73
+ @file.get_metadata(file_id)
74
+ end
75
+
76
+ def purge_file_cache(file_url)
77
+ # Purge cache from ImageKit server by file_url
78
+ @file.purge_cache(file_url)
79
+ end
80
+
81
+ def purge_file_cache_status(request_id)
82
+ @file.purge_cache_status(request_id.to_s)
83
+ end
84
+
85
+ def get_remote_file_url_metadata(remote_file_url = "")
86
+ @file.get_metadata_from_remote_url(remote_file_url)
87
+ end
88
+
89
+ # Get metadata from remote_file_url
90
+ # param remote_file_url: url string of remote file
91
+
92
+ def phash_distance(first, second)
93
+ # Get hamming distance between two phash(image hash) to check
94
+ # similarity between images
95
+
96
+ unless first && second
97
+ raise ArgumentError, Error::MISSING_PHASH_VALUE
98
+ end
99
+ hamming_distance(first, second)
100
+ end
101
+
102
+ def get_authentication_parameters(token = nil, expire = nil)
103
+ # Get Authentication params
104
+ get_authenticated_params(token, expire, @ik_req.private_key)
105
+ end
106
+ end
107
+ end
108
+
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "rest-client"
5
+ require "json"
6
+ require_relative "./constants/defaults"
7
+
8
+ # ImageKitRequest requests and sends data from server
9
+ class ImageKitRequest
10
+ attr_reader :private_key, :public_key, :url_endpoint, :transformation_position, :options
11
+
12
+ def initialize(private_key, public_key, url_endpoint, transformation_position = nil, options = nil)
13
+ @private_key = private_key
14
+ @public_key = public_key
15
+ @url_endpoint = url_endpoint
16
+ @transformation_position = transformation_position || Default::TRANSFORMATION_POSITION
17
+ @options = options || {}
18
+ end
19
+
20
+ # creates required headers
21
+ def create_headers
22
+ headers = {'Accept-Encoding': "application/json", 'Content-Type': "application/json"}
23
+ headers.update(auth_headers)
24
+ end
25
+
26
+ def auth_headers
27
+ encoded_private_key = Base64.strict_encode64(@private_key+":")
28
+ {Authorization: "Basic #{encoded_private_key}"}
29
+ end
30
+
31
+ # request method communicates with server
32
+ def request(method, url, headers = nil, payload = nil)
33
+ headers ||= create_headers
34
+ response = {response: nil, error: nil}
35
+ begin
36
+ resp = RestClient::Request.new(method: method,
37
+ url: url,
38
+ headers: headers,
39
+ payload: payload).execute
40
+
41
+
42
+ if resp.code == 404
43
+ raise RestClient::ExceptionWithResponse
44
+ elsif (resp.code >= 200) && (resp.code < 204)
45
+ response[:response] = JSON.parse(resp.body.to_s)
46
+ elsif resp.code == 204
47
+ response[:response] = {'success': true}
48
+ end
49
+
50
+ rescue RestClient::ExceptionWithResponse => err
51
+ err.http_code == 404 ? response[:error] = {'message': err.response.to_s} : JSON.parse(err.response)
52
+ end
53
+ response
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ module Imagekit
2
+ module Sdk
3
+ VERSION = '1.0.3'
4
+ end
5
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Url holds url generation method
4
+
5
+ require "cgi"
6
+ require "openssl"
7
+ require_relative "./utils/formatter"
8
+ require_relative "./constants/defaults"
9
+ require_relative "./constants/supported_transformation"
10
+ require_relative "./sdk/version.rb"
11
+
12
+ class Url
13
+ def initialize(request_obj)
14
+ @req_obj = request_obj
15
+ end
16
+
17
+ def generate_url(options)
18
+ if options.key? :src
19
+ options[:transformation_position] = Default::TRANSFORMATION_POSITION
20
+ end
21
+ extended_options = extend_url_options(options)
22
+ build_url(extended_options)
23
+ end
24
+
25
+ def build_url(options)
26
+ # build url from all options
27
+
28
+ path = options.fetch(:path, "")
29
+ src = options.fetch(:src, "")
30
+ url_endpoint = options.fetch(:url_endpoint, "")
31
+ transformation_position = options[:transformation_position]
32
+
33
+ unless Default::VALID_TRANSFORMATION_POSITION.include? transformation_position
34
+ raise ArgumentError, INVALID_TRANSFORMATION_POS
35
+ end
36
+
37
+ src_param_used_for_url = false
38
+ if (src != "") || (transformation_position == Default::QUERY_TRANSFORMATION_POSITION)
39
+ src_param_used_for_url = true
40
+ end
41
+
42
+ if path == "" && src == ""
43
+ return ""
44
+ end
45
+
46
+ result_url_hash = {'host': "", 'path': "", 'query': ""}
47
+ existing_query=nil
48
+ if path != ""
49
+ parsed_url = URI.parse(path)
50
+ existing_query=parsed_url.query
51
+ parsed_host = URI(url_endpoint)
52
+ result_url_hash[:scheme] = parsed_host.scheme
53
+
54
+ # making sure single '/' at end
55
+ result_url_hash[:host] = parsed_host.host.to_s.chomp("/") + parsed_host.path.chomp("/") + "/"
56
+ result_url_hash[:path] = trim_slash(parsed_url.path)
57
+ else
58
+ parsed_url = URI.parse(src)
59
+ existing_query=parsed_url.query
60
+ host = parsed_url.host
61
+ result_url_hash[:userinfo] = parsed_url.userinfo if parsed_url.userinfo
62
+ result_url_hash[:host] = host
63
+ result_url_hash[:scheme] = parsed_url.scheme
64
+ result_url_hash[:path] = parsed_url.path
65
+ src_param_used_for_url = true
66
+ end
67
+ query_params = {}
68
+ if existing_query!=nil
69
+ existing_query.split("&").each do |part|
70
+ parts=part.split("=")
71
+ if parts.length==2
72
+ query_params[parts[0]]=parts[1]
73
+ else
74
+ query_params[parts[0]]=""
75
+ end
76
+ end
77
+ end
78
+ options.fetch(:query_parameters, {}).each do |key, value|
79
+ query_params[key]=value
80
+ end
81
+ transformation_str = transformation_to_str(options[:transformation]).chomp("/")
82
+
83
+ if transformation_str
84
+ if (transformation_position == Default::QUERY_TRANSFORMATION_POSITION) || src_param_used_for_url == true
85
+ result_url_hash[:query] = "#{Default::TRANSFORMATION_PARAMETER}=#{transformation_str}"
86
+ query_params[:tr]=transformation_str
87
+ else
88
+ result_url_hash[:path] = "#{Default::TRANSFORMATION_PARAMETER}:#{transformation_str}/#{result_url_hash[:path]}"
89
+ end
90
+
91
+ end
92
+
93
+ result_url_hash[:host] = result_url_hash[:host].to_s.reverse.chomp("/").reverse
94
+ result_url_hash[:path] = result_url_hash[:path].chomp("/")
95
+ result_url_hash[:scheme] ||= "https"
96
+
97
+
98
+ # Signature String and Timestamp
99
+ # We can do this only for URLs that are created using urlEndpoint and path parameter
100
+ # because we need to know the endpoint to be able to remove it from the URL to create a signature
101
+ # for the remaining. With the src parameter, we would not know the "pattern" in the URL
102
+ query_param_arr = []
103
+ query_param_arr.push("ik-sdk-version=ruby-"+Imagekit::Sdk::VERSION)
104
+ if options[:signed] && !(options[:src])
105
+ intermediate_url = result_url_hash.fetch(:scheme, "") + "://" + result_url_hash.fetch(:host, "") + result_url_hash.fetch(:path, "")
106
+ if result_url_hash[:query]!=nil && result_url_hash[:query]!=""
107
+ intermediate_url += result_url_hash.fetch(:query, "")
108
+ end
109
+ end
110
+ query_params.each do |key, value|
111
+ query_param_arr.push(key.to_s + "=" + value.to_s)
112
+ end
113
+
114
+ query_param_str = query_param_arr.join("&")
115
+ result_url_hash[:query] = query_param_str
116
+ url=hash_to_url(result_url_hash)
117
+ if options[:signed]
118
+ private_key = options[:private_key]
119
+ expire_seconds = options[:expire_seconds]
120
+ expire_timestamp = get_signature_timestamp(expire_seconds)
121
+ url_signature = get_signature(private_key, url, url_endpoint, expire_timestamp)
122
+ query_param_arr.push(Default::SIGNATURE_PARAMETER + "=" + url_signature)
123
+
124
+ if expire_timestamp && (expire_timestamp != Default::TIMESTAMP)
125
+ query_param_arr.push(Default::TIMESTAMP_PARAMETER + "=" + expire_timestamp.to_s)
126
+ end
127
+
128
+ query_param_str = query_param_arr.join("&")
129
+ result_url_hash[:query] = query_param_str
130
+
131
+ url=hash_to_url(result_url_hash)
132
+ end
133
+ url
134
+ end
135
+
136
+ def transformation_to_str(transformation)
137
+ # creates transformation_position string for url
138
+ # from transformation dictionary
139
+
140
+ unless transformation.is_a?(Array)
141
+ return ""
142
+ end
143
+
144
+ parsed_transforms = []
145
+ (0..(transformation.length - 1)).each do |i|
146
+ parsed_transform_step = []
147
+
148
+ transformation[i].keys.each do |key|
149
+ transform_key = SUPPORTED_TRANS.fetch(key, nil)
150
+ transform_key ||= key
151
+
152
+ if transformation[i][key] == "-"
153
+ parsed_transform_step.push(transform_key)
154
+ else
155
+ parsed_transform_step.push("#{transform_key}#{Default::TRANSFORM_KEY_VALUE_DELIMITER}#{transformation[i][key]}")
156
+ end
157
+ end
158
+ parsed_transforms.push(parsed_transform_step.join(Default::TRANSFORM_DELIMITER))
159
+ end
160
+ parsed_transforms.join(Default::CHAIN_TRANSFORM_DELIMITER)
161
+ end
162
+
163
+ def get_signature_timestamp(seconds)
164
+ # this function returns either default time stamp
165
+ # or current unix time and expiry seconds to get
166
+ # signature time stamp
167
+
168
+ if seconds.to_i == 0
169
+ Default::DEFAULT_TIMESTAMP
170
+ else
171
+ DateTime.now.strftime("%s").to_i + seconds.to_i
172
+ end
173
+ end
174
+
175
+ def get_signature(private_key, url, url_endpoint, expiry_timestamp)
176
+ # creates signature(hashed hex key) and returns from
177
+ # private_key, url, url_endpoint and expiry_timestamp
178
+ if expiry_timestamp==0
179
+ expiry_timestamp=Default::DEFAULT_TIMESTAMP
180
+ end
181
+ if url_endpoint[url_endpoint.length-1]!="/"
182
+ url_endpoint+="/"
183
+ end
184
+ replaced_url=url.gsub(url_endpoint, "")
185
+ replaced_url = replaced_url + expiry_timestamp.to_s
186
+ OpenSSL::HMAC.hexdigest("SHA1", private_key, replaced_url)
187
+ end
188
+
189
+ def extend_url_options(options)
190
+ attr_dict = {"public_key": @req_obj.public_key,
191
+ "private_key": @req_obj.private_key,
192
+ "url_endpoint": @req_obj.url_endpoint,
193
+ "transformation_position": @req_obj.transformation_position, }
194
+ # extending url options
195
+ attr_dict.merge(options)
196
+ end
197
+
198
+ def hash_to_url(url_hash)
199
+ generated_url = url_hash.fetch(:scheme, "") + "://" + url_hash.fetch(:host, "") + url_hash.fetch(:path, "")
200
+ if url_hash[:query] != ""
201
+ generated_url = generated_url + "?" + url_hash.fetch(:query, "")
202
+ return generated_url
203
+ end
204
+ generated_url
205
+ end
206
+
207
+ def trim_slash(str, both = true)
208
+ if str == ""
209
+ return ""
210
+ end
211
+ # remove slash from a string
212
+ # if both is not provide trims both slash
213
+ # example - '/abc/' returns 'abc'
214
+ # if both=false it will only trim end slash
215
+ # example - '/abc/' returns '/abc'
216
+ # NOTE: IT'S RECOMMENDED TO USE inbuilt .chomp('string you want to remove')
217
+ # FOR REMOVING ONLY TRAILING SLASh
218
+ if both
219
+ str[0].chomp("/") + str[1..-2] + str[-1].chomp("/")
220
+ else
221
+ str.chomp("/")
222
+ end
223
+ end
224
+
225
+ # class Imagekit
226
+
227
+ # end
228
+ end
@@ -0,0 +1,36 @@
1
+ require "date"
2
+ require "securerandom"
3
+
4
+ DEFAULT_TIME_DIFF = 60 * 30
5
+
6
+ def is_valid_hex(hex_string)
7
+ # checks if hexadecimal value is valid or not
8
+ /^[[:xdigit:]]+$/ === hex_string
9
+ end
10
+
11
+ def hamming_distance(first, second)
12
+ # Calculate Hamming distance between to hex string
13
+ unless is_valid_hex(first) && is_valid_hex(second)
14
+ raise ArgumentError, "Both argument should be hexadecimal"
15
+ end
16
+ a = first.to_i(16)
17
+ b = second.to_i(16)
18
+ (a ^ b).to_s(2).count("1")
19
+ end
20
+
21
+ def get_authenticated_params(token, expire, private_key)
22
+ # return authenticated param
23
+ default_expire = DateTime.now.strftime("%s").to_i + DEFAULT_TIME_DIFF
24
+ token ||= SecureRandom.uuid
25
+
26
+ auth_params = {'token': token, 'expire': expire, 'signature': ""}
27
+ unless private_key
28
+ return nil
29
+ end
30
+
31
+ signature = OpenSSL::HMAC.hexdigest("SHA1", private_key, token.to_s + expire.to_s)
32
+ auth_params[:token] = token
33
+ auth_params[:expire] = expire || default_expire
34
+ auth_params[:signature] = signature
35
+ auth_params
36
+ end
@@ -0,0 +1,29 @@
1
+ def snake_to_camel(word)
2
+ word_list = word.split("_")
3
+ result = []
4
+ word_list&.each { |i|
5
+ if i == word_list[0]
6
+ result.push(i)
7
+ else
8
+ result.push(i.capitalize)
9
+ end
10
+ }
11
+ result.join
12
+ end
13
+
14
+ def camel_to_snake(camel_word)
15
+ # convert camel case to snake case
16
+ camel_word.to_s.gsub(/::/, "/")
17
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
18
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
19
+ .tr("-", "_")
20
+ .downcase
21
+ end
22
+
23
+ def request_formatter(data)
24
+ result = {}
25
+ data.each do |key, val|
26
+ result[snake_to_camel(key.to_s)] = val
27
+ end
28
+ result
29
+ end