Module: Jekyll::Algolia::ErrorHandler
- Includes:
- Jekyll::Algolia
- Defined in:
- lib/jekyll/algolia/error_handler.rb
Overview
Catch API errors and display messages
Constant Summary
Constants included from Jekyll::Algolia
Class Method Summary collapse
-
.error_hash(message) ⇒ Object
Public: Parses an Algolia error message into a hash of its content.
-
.identify(error, context = {}) ⇒ Object
Public: Will identify the error and return its internal name.
-
.invalid_credentials?(error, _context = {}) ⇒ Boolean
Public: Check if the credentials are working.
-
.invalid_index_name?(error, _context = {}) ⇒ Boolean
Public: Check if the index name is invalid.
-
.readable_largest_record_keys(record) ⇒ Object
Public: Returns a string explaining which attributes are the largest in the record.
-
.record_too_big?(error, context = {}) ⇒ Boolean
Public: Check if the sent records are not too big.
-
.stop(error, context = {}) ⇒ Object
Public: Stop the execution of the plugin and display if possible a human-readable error message.
-
.too_many_records?(error, _context = {}) ⇒ Boolean
Public: Check if the application has too many records.
-
.unknown_application_id?(error, _context = {}) ⇒ Boolean
Public: Check if the application id is available.
-
.unknown_setting?(error, context = {}) ⇒ Boolean
Public: Check if one of the index settings is invalid.
Methods included from Jekyll::Algolia
Class Method Details
.error_hash(message) ⇒ Object
Public: Parses an Algolia error message into a hash of its content
message - The raw message as returned by the API
Returns a hash of all parts of the message, to be more easily consumed by our error matchers
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/jekyll/algolia/error_handler.rb', line 74 def self.error_hash() = .delete("\n") # Ex: Cannot PUT to https://appid.algolia.net/1/indexes/index_name/settings: # {"message":"Invalid Application-ID or API key","status":403} (403) regex = VerEx.new do find 'Cannot ' capture('verb') { word } find ' to ' capture('scheme') { word } find '://' capture('application_id') { word } anything_but '/' find '/' capture('api_version') { digit } find '/' capture('api_section') { word } find '/' capture('index_name') do anything_but('/') end find '/' capture do capture('api_action') { word } maybe '?' capture('query_parameters') do anything_but(':') end end find ': ' capture('json') do find '{' anything_but('}') find '}' end find ' (' capture('http_error') { word } find ')' end matches = regex.match() return false unless matches # Convert matches to a hash hash = {} matches.names.each do |name| hash[name] = matches[name] end hash['api_version'] = hash['api_version'].to_i hash['http_error'] = hash['http_error'].to_i # Merging the JSON key directly in the answer hash = hash.merge(JSON.parse(hash['json'])) hash.delete('json') # Merging the query parameters in the answer CGI.parse(hash['query_parameters']).each do |key, values| hash[key] = values[0] end hash.delete('query_parameters') hash end |
.identify(error, context = {}) ⇒ Object
Public: Will identify the error and return its internal name
error - The caught error context - A hash of additional information that can be passed from the code intercepting the user
It will parse in order all potential known issues until it finds one that matches. Returns false if no match, or a hash of :name and :details further identifying the issue.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/jekyll/algolia/error_handler.rb', line 46 def self.identify(error, context = {}) known_errors = %w[ unknown_application_id invalid_credentials record_too_big too_many_records unknown_setting invalid_index_name ] # Checking the errors against our known list known_errors.each do |potential_error| error_check = send("#{potential_error}?", error, context) next if error_check == false return { name: potential_error, details: error_check } end false end |
.invalid_credentials?(error, _context = {}) ⇒ Boolean
Public: Check if the credentials are working
_context - Not used
Application ID and API key submitted don’t match any credentials known
181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/jekyll/algolia/error_handler.rb', line 181 def self.invalid_credentials?(error, _context = {}) details = error_hash(error.) return false if details == false if details['message'] != 'Invalid Application-ID or API key' return false end { 'application_id' => details['application_id'], 'index_name' => Configurator.index_name, 'index_object_ids_name' => Configurator.index_object_ids_name } end |
.invalid_index_name?(error, _context = {}) ⇒ Boolean
Public: Check if the index name is invalid
Some characters are forbidden in index names
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/jekyll/algolia/error_handler.rb', line 268 def self.invalid_index_name?(error, _context = {}) details = error_hash(error.) return false if details == false = details['message'] return false if !~ /^indexName is not valid.*/ { 'index_name' => Configurator.index_name } end |
.readable_largest_record_keys(record) ⇒ Object
Public: Returns a string explaining which attributes are the largest in the record
record - The record hash to analyze
This will be used on the ‘record_too_big` error, to guide users in finding which record is causing trouble
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/jekyll/algolia/error_handler.rb', line 145 def self.readable_largest_record_keys(record) keys = Hash[record.map { |key, value| [key, value.to_s.length] }] largest_keys = keys.sort_by { |_, value| value }.reverse[0..2] output = [] largest_keys.each do |key, size| size = Filesize.from("#{size} B").to_s('Kb') output << "#{key} (#{size})" end output.join(', ') end |
.record_too_big?(error, context = {}) ⇒ Boolean
Public: Check if the sent records are not too big
context - list of records sent in the batch
Records cannot weight more that 10Kb. If we’re getting this error it means that one of the records is too big, so we’ll try to give informations about it so the user can debug it.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/jekyll/algolia/error_handler.rb', line 203 def self.record_too_big?(error, context = {}) details = error_hash(error.) return false if details == false = details['message'] return false if !~ /^Record .* is too big .*/ # Getting the record size size, = /.*size=(.*) bytes.*/.match().captures size = Filesize.from("#{size} B").to_s('Kb') object_id = details['objectID'] # Finding the record in all the operations operation = context[:operations].find do |o| o[:action] == 'addObject' && o[:body][:objectID] == object_id end record = operation[:body] probable_wrong_keys = readable_largest_record_keys(record) # Writing the full record to disk for inspection record_log_path = Logger.write_to_file( "jekyll-algolia-record-too-big-#{object_id}.log", JSON.pretty_generate(record) ) { 'object_id' => object_id, 'object_title' => record[:title], 'object_url' => record[:url], 'probable_wrong_keys' => probable_wrong_keys, 'record_log_path' => record_log_path, 'nodes_to_index' => Configurator.algolia('nodes_to_index'), 'size' => size, 'size_limit' => '10 Kb' } end |
.stop(error, context = {}) ⇒ Object
Public: Stop the execution of the plugin and display if possible a human-readable error message
error - The caught error context - A hash of values that will be passed from where the error happened to the display
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/jekyll/algolia/error_handler.rb', line 19 def self.stop(error, context = {}) Logger.verbose("E:[jekyll-algolia] Raw error: #{error}") identified_error = identify(error, context) if identified_error == false Logger.log('E:[jekyll-algolia] Error:') Logger.log("E:#{error}") else Logger.( identified_error[:name], identified_error[:details] ) end exit 1 end |
.too_many_records?(error, _context = {}) ⇒ Boolean
Public: Check if the application has too many records
We’re trying to push too many records and it goes over quota
283 284 285 286 287 288 289 290 291 |
# File 'lib/jekyll/algolia/error_handler.rb', line 283 def self.too_many_records?(error, _context = {}) details = error_hash(error.) return false if details == false = details['message'] return false if !~ /^Record quota exceeded.*/ {} end |
.unknown_application_id?(error, _context = {}) ⇒ Boolean
Public: Check if the application id is available
_context - Not used
If the call to the cluster fails, chances are that the application ID is invalid. As we cannot actually contact the server, the error is raw and does not follow our error spec
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/jekyll/algolia/error_handler.rb', line 163 def self.unknown_application_id?(error, _context = {}) = error. return false if !~ /^Cannot reach any host/ matches = /.*\((.*)\.algolia.net.*/.match() # The API will browse on APP_ID-dsn, but push/delete on APP_ID only # We need to catch both potential errors app_id = matches[1].gsub(/-dsn$/, '') { 'application_id' => app_id } end |
.unknown_setting?(error, context = {}) ⇒ Boolean
Public: Check if one of the index settings is invalid
context - The settings passed to update the index
The API will block any call that tries to update a setting value that is not available. We’ll tell the user which one so they can fix their issue.
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/jekyll/algolia/error_handler.rb', line 247 def self.unknown_setting?(error, context = {}) details = error_hash(error.) return false if details == false = details['message'] return false if !~ /^Invalid object attributes.*/ # Getting the unknown setting name regex = /^Invalid object attributes: (.*) near line.*/ setting_name, = regex.match().captures setting_value = context[:settings][setting_name] { 'setting_name' => setting_name, 'setting_value' => setting_value } end |