imagekitio 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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