Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5501d5a
Configuration: add parameters limits
alain-andre Jan 28, 2021
c459afd
Count api calls
alain-andre Feb 3, 2021
8a68780
Action pack for remote_ip
alain-andre Feb 4, 2021
17a3a40
Expire at on api keys
alain-andre Feb 5, 2021
412ee92
Manage map tiles and router depending of the api key
alain-andre Jun 2, 2021
67da60f
Better message on OTP InternalError
alain-andre Jul 22, 2021
3781a62
Merge pull request #26 from alain-andre/manage-debug-tool-following-a…
fab-girard Jul 30, 2021
f3d862e
Add error message on isolines
alain-andre Jul 29, 2021
fef8bde
Merge pull request #29 from alain-andre/better-message-otp-error
fab-girard Sep 8, 2021
e5bf31f
Merge pull request #36 from alain-andre/add-error-message-isoline
fab-girard Sep 8, 2021
18f1a5d
Managing timeout, especially on matrices
alain-andre Sep 3, 2021
27ff2e8
Merge pull request #37 from alain-andre/manage-big-matrix
fab-girard Sep 10, 2021
7f552aa
Managing timeout, especially on matrices
alain-andre Sep 3, 2021
989c041
Merge pull request #38 from alain-andre/manage-big-matrix
fab-girard Sep 13, 2021
be48b9c
HERE: Return more details on unreachable point
alain-andre Sep 16, 2021
11c6734
Merge pull request #39 from alain-andre/here-unreachable-point
fab-girard Oct 27, 2021
256bfce
Review fixes
alain-andre Nov 16, 2021
2f31648
Merge pull request #11 from alain-andre/quotas-etc-1
fab-girard Nov 17, 2021
5798b33
Bump puma from 5.2.1 to 5.5.1
dependabot[bot] Nov 17, 2021
515ef45
Bump addressable from 2.7.0 to 2.8.0
dependabot[bot] Nov 17, 2021
29f688e
Merge pull request #43 from Mapotempo/dependabot/bundler/puma-5.5.1
fab-girard Nov 17, 2021
0644c34
Merge pull request #44 from Mapotempo/dependabot/bundler/addressable-…
fab-girard Nov 17, 2021
93f1431
Bump nokogiri from 1.11.7 to 1.12.5
dependabot[bot] Nov 17, 2021
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ build/

# Ignore application configuration
local.env

# Redis
*.aof
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ gem 'rest-client'
gem 'addressable'
gem 'border_patrol'
gem 'activesupport'
gem 'actionpack'

group :test do
gem 'rack-test'
gem 'fakeredis'
gem 'minitest'
gem 'minitest-focus'
gem 'minitest-reporters'
gem 'rack-test'
gem 'simplecov', require: false
end

Expand Down
163 changes: 99 additions & 64 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,130 +1,165 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (5.2.4.3)
actionpack (5.2.4.5)
actionview (= 5.2.4.5)
activesupport (= 5.2.4.5)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.4.5)
activesupport (= 5.2.4.5)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activesupport (5.2.4.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
ansi (1.5.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
border_patrol (0.2.1)
nokogiri (>= 1.4.3.1)
builder (3.2.3)
byebug (10.0.0)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
builder (3.2.4)
byebug (11.1.3)
concurrent-ruby (1.1.8)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
docile (1.1.5)
domain_name (0.5.20170404)
crass (1.0.6)
docile (1.3.5)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.2.1)
equalizer (0.0.11)
grape (1.2.3)
dotenv (2.7.6)
dry-configurable (0.12.1)
concurrent-ruby (~> 1.0)
dry-core (~> 0.5, >= 0.5.0)
dry-container (0.7.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.5.0)
concurrent-ruby (~> 1.0)
dry-inflector (0.2.0)
dry-logic (1.1.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.5, >= 0.5)
dry-types (1.5.0)
concurrent-ruby (~> 1.0)
dry-container (~> 0.3)
dry-core (~> 0.5, >= 0.5)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 1.0, >= 1.0.2)
erubi (1.10.0)
fakeredis (0.8.0)
redis (~> 4.1)
grape (1.5.2)
activesupport
builder
dry-types (>= 1.1)
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
virtus (>= 1.0.0)
grape-entity (0.7.1)
activesupport (>= 4.0)
grape-entity (0.8.2)
activesupport (>= 3.0.0)
multi_json (>= 1.3.2)
grape-swagger (0.32.1)
grape (>= 0.16.2)
grape-swagger-entity (0.2.3)
grape-entity (>= 0.5.0)
grape-swagger (>= 0.20.4)
grape_logging (1.7.0)
grape-swagger (1.3.1)
grape (~> 1.3)
grape-swagger-entity (0.5.1)
grape-entity (>= 0.6.0)
grape-swagger (>= 1.2.0)
grape_logging (1.8.4)
grape
rack
http-accept (1.7.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (1.6.0)
i18n (1.8.9)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
json (2.5.1)
mime-types (3.1)
loofah (2.9.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_portile2 (2.5.3)
minitest (5.11.3)
minitest-focus (1.1.2)
mime-types-data (3.2021.0212)
mini_portile2 (2.6.1)
minitest (5.14.3)
minitest-focus (1.2.1)
minitest (>= 4, < 6)
minitest-reporters (1.1.19)
minitest-reporters (1.4.3)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
multi_json (1.13.1)
mustermann (1.0.3)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
multi_json (1.15.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
mustermann-grape (1.0.1)
mustermann (>= 1.0.0)
netrc (0.11.0)
nio4r (2.5.2)
nokogiri (1.11.7)
mini_portile2 (~> 2.5.0)
nio4r (2.5.8)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
racc (~> 1.4)
polylines (0.3.0)
polylines (0.4.0)
public_suffix (4.0.6)
puma (4.3.8)
puma (5.5.1)
nio4r (~> 2.0)
racc (1.5.2)
racc (1.6.0)
rack (2.2.3)
rack-accept (0.4.5)
rack (>= 0.4)
rack-contrib (2.3.0)
rack (~> 2.0)
rack-cors (1.0.5)
rack (>= 1.6.0)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-server-pages (0.1.0)
rack
rack-test (0.8.2)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (12.3.3)
redis (4.1.2)
redis-activesupport (5.0.4)
activesupport (>= 3, < 6)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
rake (13.0.3)
redis (4.2.5)
redis-activesupport (5.2.0)
activesupport (>= 3, < 7)
redis-store (>= 1.3, < 2)
redis-store (1.4.1)
redis (>= 2.2, < 5)
rest-client (2.0.2)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
ruby-progressbar (1.9.0)
simplecov (0.15.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.4)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.2)
thread_safe (0.3.6)
tzinfo (1.2.9)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
unf_ext (0.0.7.7)

PLATFORMS
ruby

DEPENDENCIES
actionpack
activesupport
addressable
border_patrol
byebug
dotenv
fakeredis
grape
grape-entity
grape-swagger
Expand Down
100 changes: 99 additions & 1 deletion api/v01/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,97 @@
require './api/v01/isoline'
require './api/v01/capability'

require 'active_support/core_ext/string/conversions'

require 'date'

class QuotaExceeded < StandardError
attr_reader :data

def initialize(msg, data)
@data = data
super(msg)
end
end

module Api
module V01
class Api < Grape::API
before do
error!('401 Unauthorized', 401) unless params && RouterWrapper.access(true).keys.include?(params[:api_key])
if !params || !RouterWrapper.access(true).key?(params[:api_key])
error!('401 Unauthorized', 401)
elsif RouterWrapper.access[params[:api_key]][:expire_at]&.to_date&.send(:<, Date.today)
error!('402 Subscription expired', 402)
end
end

helpers do
def redis_count
RouterWrapper.config[:redis_count]
end

def count_time
@count_time ||= Time.now.utc
end

def count_base_key(operation, period = :daily)
count_date = if period == :daily
count_time.to_s[0..9]
elsif period == :monthly
count_time.to_s[0..6]
elsif period == :yearly
count_time.to_s[0..3]
end
[
[:router, operation, count_date].compact,
[:key, params[:api_key]]
].map{ |a| a.join(':') }.join('_')
end

def count_key(operation)
@count_key ||= count_base_key(operation) + '_' + [
[:ip, (env['action_dispatch.remote_ip'] || request.ip).to_s],
[:asset, params[:asset]]
].map{ |a| a.join(':') }.join('_')
end

def count(operation, raise_if_exceed = true, request_size = 1)
return unless redis_count

@count_val = redis_count.hgetall(count_key(operation)).symbolize_keys
if @count_val.empty?
@count_val = {hits: 0, transactions: 0}
redis_count.mapped_hmset @count_key, @count_val
redis_count.expire @count_key, 100.days
end
APIBase.profile(params[:api_key])[:quotas]&.each do |quota|
op = quota[:operation]
next unless op.nil? || op == operation

quota.slice(:daily, :monthly, :yearly).each do |k, v|
count = redis_count.get(count_base_key(op, k)).to_i
raise QuotaExceeded.new("Too many #{k} requests", limit: v, remaining: v - count, reset: k) if count + request_size > v
end
end if raise_if_exceed
end

def count_incr(operation, options)
return unless redis_count

count operation, false unless @count_val
incr = {hits: @count_val[:hits].to_i + 1}
incr[:transactions] = @count_val[:transactions].to_i + options[:transactions] if options[:transactions]
redis_count.mapped_hmset @count_key, incr
APIBase.profile(params[:api_key])[:quotas]&.each do |quota|
op = quota[:operation]
next unless op.nil? || op == operation

quota.slice(:daily, :monthly, :yearly).each do |k, v|
redis_count.incrby count_base_key(op, k), options[:transactions]
redis_count.expire count_base_key(op, k), 366.days
end
end if options[:transactions]
end
end

rescue_from :all, backtrace: ENV['APP_ENV'] != 'production' do |e|
Expand All @@ -49,6 +135,18 @@ class Api < Grape::API
rack_response(format_message(response, nil), 400)
elsif e.is_a?(Wrappers::UnreachablePointError)
rack_response(format_message(response, nil), 204)
elsif e.is_a?(QuotaExceeded)
headers = { 'Content-Type' => content_type,
'X-RateLimit-Limit' => e.data[:limit],
'X-RateLimit-Remaining' => e.data[:remaining],
'X-RateLimit-Reset' => if e.data[:reset] == :daily
count_time.to_date.next_day
elsif e.data[:reset] == :monthly
count_time.to_date.next_month
elsif e.data[:reset] == :yearly
count_time.to_date.next_year
end.to_time.to_i }
rack_response(format_message(response, nil), 429, headers)
else
rack_response(format_message(response, e.backtrace), 500)
end
Expand Down
Loading