Skip to content
This repository has been archived by the owner on Mar 21, 2018. It is now read-only.

feat(error): Display human error when API key ACL is not enough #20

Merged
merged 1 commit into from
Jan 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ I suggest updating the website `Gemfile` to point to the correct local directory
```ruby
gem "algoliasearch-jekyll", :path => "/path/to/local/gem/folder"
```
You should also run `rake gemspec` from the `algoliasearch-jekyll` repository if
you added new files or dependencies.


If you plan on submitting a PR, I suggest you install the git hooks. This will
run pre-commit and pre-push checks. Those checks will also be run by TravisCI,
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ source 'http://rubygems.org'
gem 'algoliasearch', '~> 1.4'
gem 'appraisal', '~> 2.1.0'
gem 'awesome_print', '~> 1.6'
gem 'jekyll', '~> 2.5'
gem 'json', '~> 1.8'
gem 'nokogiri', '~> 1.6'
gem 'verbal_expressions', '~> 0.1.5'

group :development do
gem 'coveralls', '~> 0.8'
Expand Down
18 changes: 4 additions & 14 deletions Guardfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
group :jekyll_v3 do
guard :rspec, cmd: 'appraisal jekyll-v3 bundle exec rspec --color --format documentation' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { 'spec' }
end
end

group :jekyll_v2 do
guard :rspec, cmd: 'appraisal jekyll-v2 bundle exec rspec --color --format documentation' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { 'spec' }
end
guard :rspec, cmd: 'bundle exec rspec --color --format documentation' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { 'spec' }
end

notification :off
11 changes: 10 additions & 1 deletion algoliasearch-jekyll.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Gem::Specification.new do |s|
"gemfiles/jekyll_v3.gemfile",
"lib/algoliasearch-jekyll.rb",
"lib/credential_checker.rb",
"lib/error_handler.rb",
"lib/push.rb",
"lib/record_extractor.rb",
"lib/version.rb",
Expand All @@ -53,6 +54,7 @@ Gem::Specification.new do |s|
"scripts/watch_v2",
"scripts/watch_v3",
"spec/credential_checker_spec.rb",
"spec/error_handler_spec.rb",
"spec/fixtures/jekyll_version_2/_config.yml",
"spec/fixtures/jekyll_version_2/_layouts/default.html",
"spec/fixtures/jekyll_version_2/_my-collection/collection-item.html",
Expand Down Expand Up @@ -89,7 +91,8 @@ Gem::Specification.new do |s|
"spec/spec_helper_simplecov.rb",
"txt/api_key_missing",
"txt/application_id_missing",
"txt/index_name_missing"
"txt/index_name_missing",
"txt/sample"
]
s.homepage = "https://github.com/algolia/algoliasearch-jekyll"
s.licenses = ["MIT"]
Expand All @@ -103,8 +106,10 @@ Gem::Specification.new do |s|
s.add_runtime_dependency(%q<algoliasearch>, ["~> 1.4"])
s.add_runtime_dependency(%q<appraisal>, ["~> 2.1.0"])
s.add_runtime_dependency(%q<awesome_print>, ["~> 1.6"])
s.add_runtime_dependency(%q<jekyll>, ["~> 2.5"])
s.add_runtime_dependency(%q<json>, ["~> 1.8"])
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.6"])
s.add_runtime_dependency(%q<verbal_expressions>, ["~> 0.1.5"])
s.add_development_dependency(%q<coveralls>, ["~> 0.8"])
s.add_development_dependency(%q<flay>, ["~> 2.6"])
s.add_development_dependency(%q<flog>, ["~> 4.3"])
Expand All @@ -117,8 +122,10 @@ Gem::Specification.new do |s|
s.add_dependency(%q<algoliasearch>, ["~> 1.4"])
s.add_dependency(%q<appraisal>, ["~> 2.1.0"])
s.add_dependency(%q<awesome_print>, ["~> 1.6"])
s.add_dependency(%q<jekyll>, ["~> 2.5"])
s.add_dependency(%q<json>, ["~> 1.8"])
s.add_dependency(%q<nokogiri>, ["~> 1.6"])
s.add_dependency(%q<verbal_expressions>, ["~> 0.1.5"])
s.add_dependency(%q<coveralls>, ["~> 0.8"])
s.add_dependency(%q<flay>, ["~> 2.6"])
s.add_dependency(%q<flog>, ["~> 4.3"])
Expand All @@ -132,8 +139,10 @@ Gem::Specification.new do |s|
s.add_dependency(%q<algoliasearch>, ["~> 1.4"])
s.add_dependency(%q<appraisal>, ["~> 2.1.0"])
s.add_dependency(%q<awesome_print>, ["~> 1.6"])
s.add_dependency(%q<jekyll>, ["~> 2.5"])
s.add_dependency(%q<json>, ["~> 1.8"])
s.add_dependency(%q<nokogiri>, ["~> 1.6"])
s.add_dependency(%q<verbal_expressions>, ["~> 0.1.5"])
s.add_dependency(%q<coveralls>, ["~> 0.8"])
s.add_dependency(%q<flay>, ["~> 2.6"])
s.add_dependency(%q<flog>, ["~> 4.3"])
Expand Down
3 changes: 2 additions & 1 deletion gemfiles/jekyll_v2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ source "http://rubygems.org"
gem "algoliasearch", "~> 1.4"
gem "appraisal", "~> 2.1.0"
gem "awesome_print", "~> 1.6"
gem "jekyll", "~> 2.5"
gem "json", "~> 1.8"
gem "nokogiri", "~> 1.6"
gem "jekyll", "~> 2.5"
gem "verbal_expressions", "~> 0.1.5"

group :development do
gem "coveralls", "~> 0.8"
Expand Down
3 changes: 2 additions & 1 deletion gemfiles/jekyll_v3.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ source "http://rubygems.org"
gem "algoliasearch", "~> 1.4"
gem "appraisal", "~> 2.1.0"
gem "awesome_print", "~> 1.6"
gem "jekyll", "~> 3.0"
gem "json", "~> 1.8"
gem "nokogiri", "~> 1.6"
gem "jekyll", "~> 3.0"
gem "verbal_expressions", "~> 0.1.5"
gem "jekyll-paginate", "~> 1.1.0"

group :development do
Expand Down
22 changes: 6 additions & 16 deletions lib/credential_checker.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
require 'algoliasearch'
require 'nokogiri'
require 'json'
require_relative './error_handler.rb'

# Given an HTML file as input, will return an array of records to index
class AlgoliaSearchCredentialChecker
attr_accessor :config
attr_accessor :config, :logger

def initialize(config)
@config = config
@logger = AlgoliaSearchErrorHandler.new
end

# Read the API key either from ENV or from an _algolia_api_key file in
Expand All @@ -24,36 +26,24 @@ def api_key
nil
end

def display_error(file)
file = File.expand_path(File.join(File.dirname(__FILE__), '../txt', file))
content = File.open(file).readlines.map(&:chomp)
content.each_with_index do |line, index|
if index == 0
Jekyll.logger.error line
next
end
Jekyll.logger.warn line
end
end

# Check that the API key is available
def check_api_key
return if api_key
display_error('api_key_missing')
@logger.display('api_key_missing')
exit 1
end

# Check that the application id is defined
def check_application_id
return if @config['algolia'] && @config['algolia']['application_id']
display_error('application_id_missing')
@logger.display('application_id_missing')
exit 1
end

# Check that the index name is defined
def check_index_name
return if @config['algolia'] && @config['algolia']['index_name']
display_error('index_name_missing')
@logger.display('index_name_missing')
exit 1
end

Expand Down
87 changes: 87 additions & 0 deletions lib/error_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
require 'json'
require 'verbal_expressions'

# Helps in displaying useful error messages to users, to help them debug their
# issues
class AlgoliaSearchErrorHandler
# Will output the specified error file.
# First line is displayed as error, next ones as warning
def display(file)
file = File.expand_path(File.join(File.dirname(__FILE__), '../txt', file))
content = File.open(file).readlines.map(&:chomp)
content.each_with_index do |line, index|
if index == 0
Jekyll.logger.error line
next
end
Jekyll.logger.warn line
end
end

def error_tester
# Ex: Cannot PUT to https://appid.algolia.net/1/indexes/index_name/settings:
# {"message":"Invalid Application-ID or API key","status":403} (403)
VerEx.new do
find 'Cannot '
capture('verb') { word }
find ' to '
capture('scheme') { word }
find '://'
capture('app_id') { word }
anything_but '/'
find '/'
capture('api_version') { digit }
find '/'
capture('api_section') { word }
find '/'
capture('index_name') { word }
find '/'
capture('api_action') { word }
find ': '
capture('json') do
find '{'
anything_but('}')
find '}'
end
find ' ('
capture('http_error') { word }
find ')'
end
end

def parse_algolia_error(error)
error.gsub!("\n", '')

tester = error_tester
matches = tester.match(error)

return false unless matches

hash = {}
matches.names.each do |match|
hash[match] = matches[match]
end

# Cast integers
hash['api_version'] = hash['api_version'].to_i
hash['http_error'] = hash['http_error'].to_i

# Parse JSON
hash['json'] = JSON.parse(hash['json'])

hash
end

# Given an Algolia API error message, will return the best error message
def readable_algolia_error(error)
error = parse_algolia_error(error)
return false unless error

# Given API key does not have rights on the _tmp index
if error['http_error'] == 403 && error['index_name'] =~ /_tmp$/
return 'check_key_acl_to_tmp_index'
end

false
end
end
24 changes: 21 additions & 3 deletions lib/push.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_relative './version'
require_relative './record_extractor'
require_relative './credential_checker'
require_relative './error_handler'

# `jekyll algolia push` command
class AlgoliaSearchJekyllPush < Jekyll::Command
Expand Down Expand Up @@ -126,7 +127,25 @@ def configure_index(index)
end
end

index.set_settings(settings)
begin
index.set_settings(settings)
rescue StandardError => error
display_error(error)
exit 1
end
end

# Display the error in a human-friendly way if possible
def display_error(error)
error_handler = AlgoliaSearchErrorHandler.new
readable_error = error_handler.readable_algolia_error(error.message)

if readable_error
error_handler.display(readable_error)
else
Jekyll.logger.error 'Algolia Error: HTTP Error'
Jekyll.logger.warn error.message
end
end

# Change the User-Agent header to isolate calls from this plugin
Expand All @@ -150,8 +169,7 @@ def batch_add_items(items, index)
begin
index.add_objects!(batch) unless @is_dry_run
rescue StandardError => error
Jekyll.logger.error 'Algolia Error: HTTP Error'
Jekyll.logger.warn error.message
display_error(error)
exit 1
end
end
Expand Down
3 changes: 3 additions & 0 deletions scripts/coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

COVERAGE=1 appraisal jekyll-v2 bundle exec rspec
2 changes: 1 addition & 1 deletion scripts/test_ci
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
# worrying about appraisal
cd "$(dirname "$BASH_SOURCE")"/..

bundle exec rspec
COVERAGE=1 bundle exec rspec
2 changes: 1 addition & 1 deletion scripts/test_v2
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
cd "$(dirname "$BASH_SOURCE")"/..

echo "Testing under Jekyll 2.5"
appraisal jekyll-v2 bundle exec rspec
COVERAGE=1 appraisal jekyll-v2 bundle exec rspec
2 changes: 1 addition & 1 deletion scripts/test_v3
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
cd "$(dirname "$BASH_SOURCE")"/..

echo "Testing under Jekyll 3.0"
appraisal jekyll-v3 bundle exec rspec
COVERAGE=1 appraisal jekyll-v3 bundle exec rspec

2 changes: 1 addition & 1 deletion scripts/watch_v2
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
cd "$(dirname "$BASH_SOURCE")"/..

guard -g jekyll_v2
appraisal jekyll-v2 guard
2 changes: 1 addition & 1 deletion scripts/watch_v3
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
cd "$(dirname "$BASH_SOURCE")"/..

guard -g jekyll_v3
appraisal jekyll-v3 guard
18 changes: 11 additions & 7 deletions spec/credential_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,16 @@
end

describe 'assert_valid' do
before(:each) do
allow(checker.logger).to receive(:display)
end
it 'should display error if no api key' do
# Given
allow(checker).to receive(:api_key) { nil }
allow(checker).to receive(:api_key).and_return nil

# Then
expect(Jekyll.logger).to receive(:error).with(/api key/i)
expect(Jekyll.logger).to receive(:warn).at_least(:once)
expect(-> { checker.assert_valid }).to raise_error SystemExit
expect(checker.logger).to have_received(:display).with('api_key_missing')
end

it 'should display error if no application id' do
Expand All @@ -79,9 +81,10 @@
stub_const('ENV', 'ALGOLIA_API_KEY' => 'APIKEY_FROM_ENV')

# Then
expect(Jekyll.logger).to receive(:error).with(/application id/i)
expect(Jekyll.logger).to receive(:warn).at_least(:once)
expect(-> { checker.assert_valid }).to raise_error SystemExit
expect(checker.logger)
.to have_received(:display)
.with('application_id_missing')
end

it 'should display error if no index name' do
Expand All @@ -93,9 +96,10 @@
stub_const('ENV', 'ALGOLIA_API_KEY' => 'APIKEY_FROM_ENV')

# Then
expect(Jekyll.logger).to receive(:error).with(/index name/i)
expect(Jekyll.logger).to receive(:warn).at_least(:once)
expect(-> { checker.assert_valid }).to raise_error SystemExit
expect(checker.logger)
.to have_received(:display)
.with('index_name_missing')
end

it 'should init the Algolia client' do
Expand Down
Loading