apress-api 1.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.drone.yml +30 -0
- data/.gitignore +15 -0
- data/.rspec +4 -0
- data/Appraisals +31 -0
- data/CHANGELOG.md +227 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/app/controllers/apress/api/deprecated_versions_controller.rb +15 -0
- data/app/controllers/apress/api/v1/callbacks_controller.rb +30 -0
- data/app/controllers/apress/api/v1/tokens_controller.rb +24 -0
- data/app/docs/schema/api/v1/types/apress/api/link.rb +29 -0
- data/app/docs/schema/api/v1/types/apress/api/links.rb +24 -0
- data/app/docs/swagger/v1/controllers/apress/api/tokens_controller.rb +64 -0
- data/app/docs/swagger/v1/default_responses/bad_request.rb +16 -0
- data/app/docs/swagger/v1/default_responses/not_found.rb +16 -0
- data/app/docs/swagger/v1/default_responses/unauthenticated.rb +16 -0
- data/app/docs/swagger/v1/default_responses/unauthorized.rb +16 -0
- data/app/docs/swagger/v1/default_responses/unprocessable.rb +16 -0
- data/app/docs/swagger/v1/default_responses/updates_locked.rb +13 -0
- data/app/docs/swagger/v1/models/apress/api/client.rb +53 -0
- data/app/docs/swagger/v1/models/apress/api/simple_error.rb +27 -0
- data/app/docs/swagger/v1/models/apress/api/unproccesable_error.rb +16 -0
- data/app/docs/swagger/v1/models/apress/api/unprocessable_error.rb +37 -0
- data/app/docs/swagger/v1/root.rb +28 -0
- data/app/interactors/apress/api/callbacks/base_callback.rb +42 -0
- data/app/interactors/apress/api/delayed_fire_callback.rb +25 -0
- data/app/jobs/apress/api/event_handler_enqueueing_job.rb +19 -0
- data/app/jobs/apress/api/fire_callback_job.rb +25 -0
- data/app/models/apress/api/client.rb +60 -0
- data/app/policies/apress/api/callback_policy.rb +15 -0
- data/app/services/apress/api/auth_service.rb +37 -0
- data/app/views/apress/api/shared/_exception.json.jbuilder +4 -0
- data/app/views/apress/api/shared/error.json.jbuilder +5 -0
- data/app/views/apress/api/shared/parameter_missing_errors.json.jbuilder +9 -0
- data/app/views/apress/api/shared/unprocessable_errors.json.jbuilder +9 -0
- data/app/views/apress/api/v1/clients/_client.json.jbuilder +2 -0
- data/app/views/apress/api/v1/tokens/create.json.jbuilder +4 -0
- data/apress-api.gemspec +46 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20150716000000_create_api_clients.rb +38 -0
- data/dip.yml +48 -0
- data/docker-compose.development.yml +18 -0
- data/docker-compose.drone.yml +7 -0
- data/docker-compose.yml +20 -0
- data/lib/apress-api.rb +1 -0
- data/lib/apress/api.rb +25 -0
- data/lib/apress/api/api_controller/authentification.rb +33 -0
- data/lib/apress/api/api_controller/base.rb +63 -0
- data/lib/apress/api/api_controller/compatibility.rb +26 -0
- data/lib/apress/api/api_controller/pagination.rb +60 -0
- data/lib/apress/api/api_controller/pagination_helper.rb +55 -0
- data/lib/apress/api/api_controller/rescue.rb +89 -0
- data/lib/apress/api/api_controller/responds.rb +25 -0
- data/lib/apress/api/callbacks/config.rb +56 -0
- data/lib/apress/api/callbacks/fire_callback_error.rb +12 -0
- data/lib/apress/api/callbacks/integration.rb +28 -0
- data/lib/apress/api/callbacks/repeat_callback_error.rb +12 -0
- data/lib/apress/api/engine.rb +33 -0
- data/lib/apress/api/extensions/jbuilder/jbuilder_template.rb +41 -0
- data/lib/apress/api/rspec.rb +48 -0
- data/lib/apress/api/rspec/utils.rb +17 -0
- data/lib/apress/api/testing/json_matcher.rb +9 -0
- data/lib/apress/api/version.rb +5 -0
- data/lib/tasks/docs.rake +12 -0
- data/spec/controllers/api_controller/authentification_spec.rb +79 -0
- data/spec/controllers/api_controller/pagination_spec.rb +199 -0
- data/spec/controllers/api_controller/rescue_spec.rb +167 -0
- data/spec/controllers/deprecated_versions_controller_spec.rb +10 -0
- data/spec/controllers/v1/callbacks_controller_spec.rb +50 -0
- data/spec/controllers/v1/tokens_controller_spec.rb +53 -0
- data/spec/factories/client_factory.rb +4 -0
- data/spec/helpers/paginating_cache_spec.rb +72 -0
- data/spec/interactors/apress/api/delayed_fire_callback_spec.rb +43 -0
- data/spec/internal/app/integrations/error_client/fire_callback.rb +14 -0
- data/spec/internal/app/integrations/service_client/fire_callback.rb +7 -0
- data/spec/internal/app/jobs/handler_job.rb +5 -0
- data/spec/internal/app/jobs/second_handler_job.rb +5 -0
- data/spec/internal/app/models/dummy_model.rb +15 -0
- data/spec/internal/config/database.yml +5 -0
- data/spec/internal/config/environments/test.rb +5 -0
- data/spec/internal/config/initializers/api.rb +10 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/db/schema.rb +5 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/jobs/apress/api/event_handler_equeueing_job_spec.rb +31 -0
- data/spec/jobs/apress/api/fire_callback_job_spec.rb +34 -0
- data/spec/lib/apress/api/callbacks/integration_spec.rb +24 -0
- data/spec/models/client_spec.rb +25 -0
- data/spec/services/auth_service_spec.rb +64 -0
- data/spec/spec_helper.rb +34 -0
- metadata +518 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module Apress
|
2
|
+
module Api
|
3
|
+
class EventHandlerEnqueueingJob
|
4
|
+
include Resque::Integration
|
5
|
+
|
6
|
+
queue :api_callbacks
|
7
|
+
|
8
|
+
def self.perform(handler, event_params)
|
9
|
+
job = handler.camelize.constantize
|
10
|
+
|
11
|
+
if job.respond_to?(:enqueue)
|
12
|
+
job.enqueue(event_params)
|
13
|
+
else
|
14
|
+
Resque.enqueue(job, event_params)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Apress
|
2
|
+
module Api
|
3
|
+
class FireCallbackJob
|
4
|
+
include Resque::Integration
|
5
|
+
extend Resque::Plugins::ExponentialBackoff
|
6
|
+
|
7
|
+
queue :api_callbacks
|
8
|
+
|
9
|
+
@retry_exceptions = {
|
10
|
+
Apress::Api::Callbacks::FireCallbackError => [1, 60, 300, 3600],
|
11
|
+
Apress::Api::Callbacks::RepeatCallbackError => 300
|
12
|
+
}
|
13
|
+
@ignore_exceptions = [Apress::Api::Callbacks::RepeatCallbackError]
|
14
|
+
|
15
|
+
def self.perform(service, event, params)
|
16
|
+
callback_class = "#{service}_client/fire_callback".camelize.constantize
|
17
|
+
|
18
|
+
callback_class.call!(
|
19
|
+
event: event,
|
20
|
+
params: params
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Apress
|
2
|
+
module Api
|
3
|
+
class Client < ActiveRecord::Base
|
4
|
+
self.table_name = "api_clients"
|
5
|
+
|
6
|
+
validates :access_id, presence: true
|
7
|
+
validates :secret_token, presence: true
|
8
|
+
validates :secret_token_expire_at, presence: true
|
9
|
+
validates :refresh_token, presence: true
|
10
|
+
validates :refresh_token_expire_at, presence: true
|
11
|
+
|
12
|
+
before_validation :generate_access_id, on: :create
|
13
|
+
before_validation :regenerate_tokens, on: :create
|
14
|
+
|
15
|
+
def regenerate_tokens
|
16
|
+
generate_secret_token
|
17
|
+
set_secret_token_expiration
|
18
|
+
generate_refresh_token
|
19
|
+
set_refresh_token_expiration
|
20
|
+
end
|
21
|
+
|
22
|
+
def regenerate_tokens!
|
23
|
+
regenerate_tokens
|
24
|
+
save!
|
25
|
+
end
|
26
|
+
|
27
|
+
def refresh_token_expired?
|
28
|
+
refresh_token_expire_at < Time.now.utc
|
29
|
+
end
|
30
|
+
|
31
|
+
def secret_token_expired?
|
32
|
+
secret_token_expire_at < Time.now.utc
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def generate_access_id
|
38
|
+
self.access_id = SecureRandom.uuid
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_secret_token
|
42
|
+
self.secret_token = ::ApiAuth.generate_secret_key
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_secret_token_expiration
|
46
|
+
self.secret_token_expire_at = Rails.application.config.api[:secret_token_ttl].from_now
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_refresh_token
|
50
|
+
self.refresh_token = ::ApiAuth.generate_secret_key
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_refresh_token_expiration
|
54
|
+
self.refresh_token_expire_at = Rails.application.config.api[:refresh_token_ttl].from_now
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveSupport.run_load_hooks(:"apress/api/client", self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Apress
|
2
|
+
module Api
|
3
|
+
class AuthService
|
4
|
+
rattr_initialize :request
|
5
|
+
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
delegate :query_parameters, to: :request
|
9
|
+
|
10
|
+
# Find Client by access_id, check sercret_key
|
11
|
+
#
|
12
|
+
# Returns boolean
|
13
|
+
def call
|
14
|
+
return false unless access_id
|
15
|
+
|
16
|
+
@client = Apress::Api::Client.find_by_access_id(access_id)
|
17
|
+
return false unless client
|
18
|
+
|
19
|
+
return false if client.secret_token_expired?
|
20
|
+
|
21
|
+
return true if not_check_signature?
|
22
|
+
::ApiAuth.authentic?(request, client.secret_token)
|
23
|
+
end
|
24
|
+
|
25
|
+
def access_id
|
26
|
+
@access_id ||= ApiAuth.access_id(request) || query_parameters[:access_id]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def not_check_signature?
|
32
|
+
check_signature = query_parameters[:check_signature]
|
33
|
+
check_signature.present? && check_signature.to_i.zero? && (Rails.env.staging? || !Rails.env.production?)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/apress-api.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'apress/api/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "apress-api"
|
7
|
+
spec.version = Apress::Api::VERSION
|
8
|
+
spec.authors = ["merkushin"]
|
9
|
+
spec.email = ["merkushin.m.s@gmail.com"]
|
10
|
+
spec.summary = "Apress-Api"
|
11
|
+
spec.homepage = "https://github.com/abak-press/apress-api"
|
12
|
+
spec.license = "MIT"
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_runtime_dependency "rails", ">= 3.1.0", "< 6.0.0"
|
20
|
+
spec.add_runtime_dependency 'pg'
|
21
|
+
spec.add_runtime_dependency "api-auth", ">= 1.3.1"
|
22
|
+
spec.add_runtime_dependency "oj", ">= 2.9.9"
|
23
|
+
spec.add_runtime_dependency "multi_json", ">= 1.11.2"
|
24
|
+
spec.add_runtime_dependency "jbuilder", ">= 2.3.1"
|
25
|
+
spec.add_runtime_dependency "attr_extras", ">= 4.4.0"
|
26
|
+
spec.add_runtime_dependency 'swagger-core', '>= 0.3.0'
|
27
|
+
spec.add_runtime_dependency 'interactor'
|
28
|
+
spec.add_runtime_dependency 'pundit'
|
29
|
+
|
30
|
+
spec.add_runtime_dependency 'resque-integration'
|
31
|
+
spec.add_runtime_dependency 'apress-documentation', '>= 0.2.0'
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
34
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
+
spec.add_development_dependency "rspec", ">= 3.2"
|
36
|
+
spec.add_development_dependency "rspec-rails", ">= 3.2"
|
37
|
+
spec.add_development_dependency "combustion", ">= 0.5.4"
|
38
|
+
spec.add_development_dependency "appraisal"
|
39
|
+
spec.add_development_dependency "timecop"
|
40
|
+
spec.add_development_dependency "simplecov", ">= 0.9"
|
41
|
+
spec.add_development_dependency "factory_girl_rails", ">= 4.5"
|
42
|
+
spec.add_development_dependency "json-schema"
|
43
|
+
spec.add_development_dependency "test-unit"
|
44
|
+
spec.add_development_dependency "mock_redis"
|
45
|
+
spec.add_development_dependency 'pry-byebug'
|
46
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
current_api_routes = lambda do
|
3
|
+
resources :clients, only: [] do
|
4
|
+
resources :tokens, only: [:create]
|
5
|
+
end
|
6
|
+
|
7
|
+
post 'callbacks/:service' => 'callbacks#create'
|
8
|
+
end
|
9
|
+
|
10
|
+
scope module: "apress", constraints: {domain: :current} do
|
11
|
+
namespace :api do
|
12
|
+
scope module: :v1, ¤t_api_routes
|
13
|
+
namespace :v1, ¤t_api_routes
|
14
|
+
resource :deprecated_version, only: :show
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
if Rails::VERSION::MAJOR > 4
|
2
|
+
MIGRATION_CLASS = ActiveRecord::Migration[4.2]
|
3
|
+
else
|
4
|
+
MIGRATION_CLASS = ActiveRecord::Migration
|
5
|
+
end
|
6
|
+
|
7
|
+
class CreateApiClients < MIGRATION_CLASS
|
8
|
+
def up
|
9
|
+
create_table :api_clients do |t|
|
10
|
+
t.string :access_id, null: false, limit: 36
|
11
|
+
t.string :secret_token, null: false
|
12
|
+
t.datetime :secret_token_expire_at, null: false
|
13
|
+
t.string :refresh_token, null: false
|
14
|
+
t.datetime :refresh_token_expire_at, null: false
|
15
|
+
t.string :user_agent
|
16
|
+
t.timestamps null: false
|
17
|
+
end
|
18
|
+
|
19
|
+
execute <<-SQL
|
20
|
+
COMMENT ON TABLE api_clients IS 'Пользователи API';
|
21
|
+
COMMENT ON COLUMN api_clients.access_id IS 'Уникальный ID пользователя';
|
22
|
+
COMMENT ON COLUMN api_clients.secret_token IS 'Секретный ключ для подписи запроса';
|
23
|
+
COMMENT ON COLUMN api_clients.secret_token_expire_at IS 'Время протухания';
|
24
|
+
COMMENT ON COLUMN api_clients.refresh_token IS 'Ключ для получения нового секретного ключа';
|
25
|
+
COMMENT ON COLUMN api_clients.refresh_token_expire_at IS 'Время протухания';
|
26
|
+
COMMENT ON COLUMN api_clients.user_agent IS 'User-Agent пользователя';
|
27
|
+
|
28
|
+
ALTER TABLE api_clients
|
29
|
+
ADD CONSTRAINT uniq_api_clients_on_access_id
|
30
|
+
UNIQUE(access_id)
|
31
|
+
DEFERRABLE INITIALLY DEFERRED;
|
32
|
+
SQL
|
33
|
+
end
|
34
|
+
|
35
|
+
def down
|
36
|
+
drop_table :api_clients
|
37
|
+
end
|
38
|
+
end
|
data/dip.yml
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
version: '1'
|
2
|
+
|
3
|
+
environment:
|
4
|
+
DOCKER_RUBY_VERSION: 2.2
|
5
|
+
RUBY_IMAGE_TAG: 2.2-latest
|
6
|
+
POSTGRES_IMAGE_TAG: 9.6-0.7.0
|
7
|
+
COMPOSE_FILE_EXT: development
|
8
|
+
RAILS_ENV: test
|
9
|
+
APRESS_GEMS_CREDENTIALS: ""
|
10
|
+
|
11
|
+
compose:
|
12
|
+
files:
|
13
|
+
- docker-compose.yml
|
14
|
+
- docker-compose.${COMPOSE_FILE_EXT}.yml
|
15
|
+
|
16
|
+
interaction:
|
17
|
+
sh:
|
18
|
+
service: app
|
19
|
+
|
20
|
+
irb:
|
21
|
+
service: app
|
22
|
+
command: irb
|
23
|
+
|
24
|
+
bundle:
|
25
|
+
service: app
|
26
|
+
command: bundle
|
27
|
+
|
28
|
+
rake:
|
29
|
+
service: app
|
30
|
+
command: bundle exec rake
|
31
|
+
|
32
|
+
appraisal:
|
33
|
+
service: app
|
34
|
+
command: bundle exec appraisal
|
35
|
+
|
36
|
+
rspec:
|
37
|
+
service: app
|
38
|
+
command: bundle exec appraisal bundle exec rspec
|
39
|
+
|
40
|
+
clean:
|
41
|
+
service: app
|
42
|
+
command: rm -f Gemfile.lock gemfiles/*.gemfile.*
|
43
|
+
|
44
|
+
provision:
|
45
|
+
- docker volume create --name bundler_data
|
46
|
+
- dip clean
|
47
|
+
- dip bundle install
|
48
|
+
- dip appraisal install
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
version: '2'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
image: abakpress/ruby-app:$RUBY_IMAGE_TAG
|
6
|
+
environment:
|
7
|
+
- SSH_AUTH_SOCK=/ssh/auth/sock
|
8
|
+
- BUNDLE_PATH=/bundle/$DOCKER_RUBY_VERSION
|
9
|
+
- BUNDLE_CONFIG=/app/.bundle/config
|
10
|
+
- TEST_DB_HOST=db
|
11
|
+
- TEST_DB_NAME=docker
|
12
|
+
- TEST_DB_USERNAME=postgres
|
13
|
+
command: bash
|
14
|
+
depends_on:
|
15
|
+
- db
|
16
|
+
|
17
|
+
db:
|
18
|
+
image: abakpress/postgres-db:$POSTGRES_IMAGE_TAG
|
19
|
+
environment:
|
20
|
+
- POSTGRES_DB=docker
|
data/lib/apress-api.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'apress/api'
|
data/lib/apress/api.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/lazy_load_patch" if ActiveSupport::VERSION::MAJOR == 3 && ActiveSupport::VERSION::MINOR == 1
|
4
|
+
require "multi_json"
|
5
|
+
require "jbuilder"
|
6
|
+
require "api_auth"
|
7
|
+
require "attr_extras"
|
8
|
+
require "addressable"
|
9
|
+
require 'interactor'
|
10
|
+
require 'pundit'
|
11
|
+
require 'swagger/blocks'
|
12
|
+
require 'resque/integration'
|
13
|
+
require 'apress/documentation'
|
14
|
+
require "apress/api/version"
|
15
|
+
require "apress/api/engine"
|
16
|
+
|
17
|
+
module Apress
|
18
|
+
module Api
|
19
|
+
module Swagger
|
20
|
+
def self.const_missing(name)
|
21
|
+
::Apress::Documentation::Swagger.const_get(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|