apress-api 1.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +30 -0
  3. data/.gitignore +15 -0
  4. data/.rspec +4 -0
  5. data/Appraisals +31 -0
  6. data/CHANGELOG.md +227 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +31 -0
  10. data/Rakefile +2 -0
  11. data/app/controllers/apress/api/deprecated_versions_controller.rb +15 -0
  12. data/app/controllers/apress/api/v1/callbacks_controller.rb +30 -0
  13. data/app/controllers/apress/api/v1/tokens_controller.rb +24 -0
  14. data/app/docs/schema/api/v1/types/apress/api/link.rb +29 -0
  15. data/app/docs/schema/api/v1/types/apress/api/links.rb +24 -0
  16. data/app/docs/swagger/v1/controllers/apress/api/tokens_controller.rb +64 -0
  17. data/app/docs/swagger/v1/default_responses/bad_request.rb +16 -0
  18. data/app/docs/swagger/v1/default_responses/not_found.rb +16 -0
  19. data/app/docs/swagger/v1/default_responses/unauthenticated.rb +16 -0
  20. data/app/docs/swagger/v1/default_responses/unauthorized.rb +16 -0
  21. data/app/docs/swagger/v1/default_responses/unprocessable.rb +16 -0
  22. data/app/docs/swagger/v1/default_responses/updates_locked.rb +13 -0
  23. data/app/docs/swagger/v1/models/apress/api/client.rb +53 -0
  24. data/app/docs/swagger/v1/models/apress/api/simple_error.rb +27 -0
  25. data/app/docs/swagger/v1/models/apress/api/unproccesable_error.rb +16 -0
  26. data/app/docs/swagger/v1/models/apress/api/unprocessable_error.rb +37 -0
  27. data/app/docs/swagger/v1/root.rb +28 -0
  28. data/app/interactors/apress/api/callbacks/base_callback.rb +42 -0
  29. data/app/interactors/apress/api/delayed_fire_callback.rb +25 -0
  30. data/app/jobs/apress/api/event_handler_enqueueing_job.rb +19 -0
  31. data/app/jobs/apress/api/fire_callback_job.rb +25 -0
  32. data/app/models/apress/api/client.rb +60 -0
  33. data/app/policies/apress/api/callback_policy.rb +15 -0
  34. data/app/services/apress/api/auth_service.rb +37 -0
  35. data/app/views/apress/api/shared/_exception.json.jbuilder +4 -0
  36. data/app/views/apress/api/shared/error.json.jbuilder +5 -0
  37. data/app/views/apress/api/shared/parameter_missing_errors.json.jbuilder +9 -0
  38. data/app/views/apress/api/shared/unprocessable_errors.json.jbuilder +9 -0
  39. data/app/views/apress/api/v1/clients/_client.json.jbuilder +2 -0
  40. data/app/views/apress/api/v1/tokens/create.json.jbuilder +4 -0
  41. data/apress-api.gemspec +46 -0
  42. data/config/routes.rb +17 -0
  43. data/db/migrate/20150716000000_create_api_clients.rb +38 -0
  44. data/dip.yml +48 -0
  45. data/docker-compose.development.yml +18 -0
  46. data/docker-compose.drone.yml +7 -0
  47. data/docker-compose.yml +20 -0
  48. data/lib/apress-api.rb +1 -0
  49. data/lib/apress/api.rb +25 -0
  50. data/lib/apress/api/api_controller/authentification.rb +33 -0
  51. data/lib/apress/api/api_controller/base.rb +63 -0
  52. data/lib/apress/api/api_controller/compatibility.rb +26 -0
  53. data/lib/apress/api/api_controller/pagination.rb +60 -0
  54. data/lib/apress/api/api_controller/pagination_helper.rb +55 -0
  55. data/lib/apress/api/api_controller/rescue.rb +89 -0
  56. data/lib/apress/api/api_controller/responds.rb +25 -0
  57. data/lib/apress/api/callbacks/config.rb +56 -0
  58. data/lib/apress/api/callbacks/fire_callback_error.rb +12 -0
  59. data/lib/apress/api/callbacks/integration.rb +28 -0
  60. data/lib/apress/api/callbacks/repeat_callback_error.rb +12 -0
  61. data/lib/apress/api/engine.rb +33 -0
  62. data/lib/apress/api/extensions/jbuilder/jbuilder_template.rb +41 -0
  63. data/lib/apress/api/rspec.rb +48 -0
  64. data/lib/apress/api/rspec/utils.rb +17 -0
  65. data/lib/apress/api/testing/json_matcher.rb +9 -0
  66. data/lib/apress/api/version.rb +5 -0
  67. data/lib/tasks/docs.rake +12 -0
  68. data/spec/controllers/api_controller/authentification_spec.rb +79 -0
  69. data/spec/controllers/api_controller/pagination_spec.rb +199 -0
  70. data/spec/controllers/api_controller/rescue_spec.rb +167 -0
  71. data/spec/controllers/deprecated_versions_controller_spec.rb +10 -0
  72. data/spec/controllers/v1/callbacks_controller_spec.rb +50 -0
  73. data/spec/controllers/v1/tokens_controller_spec.rb +53 -0
  74. data/spec/factories/client_factory.rb +4 -0
  75. data/spec/helpers/paginating_cache_spec.rb +72 -0
  76. data/spec/interactors/apress/api/delayed_fire_callback_spec.rb +43 -0
  77. data/spec/internal/app/integrations/error_client/fire_callback.rb +14 -0
  78. data/spec/internal/app/integrations/service_client/fire_callback.rb +7 -0
  79. data/spec/internal/app/jobs/handler_job.rb +5 -0
  80. data/spec/internal/app/jobs/second_handler_job.rb +5 -0
  81. data/spec/internal/app/models/dummy_model.rb +15 -0
  82. data/spec/internal/config/database.yml +5 -0
  83. data/spec/internal/config/environments/test.rb +5 -0
  84. data/spec/internal/config/initializers/api.rb +10 -0
  85. data/spec/internal/config/routes.rb +3 -0
  86. data/spec/internal/db/schema.rb +5 -0
  87. data/spec/internal/log/.gitignore +1 -0
  88. data/spec/jobs/apress/api/event_handler_equeueing_job_spec.rb +31 -0
  89. data/spec/jobs/apress/api/fire_callback_job_spec.rb +34 -0
  90. data/spec/lib/apress/api/callbacks/integration_spec.rb +24 -0
  91. data/spec/models/client_spec.rb +25 -0
  92. data/spec/services/auth_service_spec.rb +64 -0
  93. data/spec/spec_helper.rb +34 -0
  94. 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,15 @@
1
+ module Apress
2
+ module Api
3
+ class CallbackPolicy
4
+ attr_reader :client
5
+
6
+ def initialize(client, _callback)
7
+ @client = client
8
+ end
9
+
10
+ def create?
11
+ Apress::Api::Callbacks::Config.allowed_client?(client)
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -0,0 +1,4 @@
1
+ json.error do
2
+ json.message exception.message
3
+ json.backtrace exception.backtrace
4
+ end
@@ -0,0 +1,5 @@
1
+ json.status @status
2
+
3
+ if @exception && show_errors?
4
+ json.partial! partial: "apress/api/shared/exception", locals: {exception: @exception}
5
+ end
@@ -0,0 +1,9 @@
1
+ json.status @status
2
+
3
+ json.errors do
4
+ json.array! @errors do |error|
5
+ error.each do |k, v|
6
+ json.set! k, v
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ json.status @status
2
+
3
+ json.errors do
4
+ json.array! @errors do |error|
5
+ error.each do |k, v|
6
+ json.set! k, v
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ json.(client, :id, :access_id, :secret_token_expire_at, :refresh_token_expire_at, :user_agent,
2
+ :updated_at, :created_at)
@@ -0,0 +1,4 @@
1
+ json.client do
2
+ json.partial! partial: "apress/api/v1/clients/client", locals: {client: @client}
3
+ json.(@client, :secret_token, :refresh_token)
4
+ end
@@ -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, &current_api_routes
13
+ namespace :v1, &current_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
@@ -0,0 +1,18 @@
1
+ version: '2'
2
+
3
+ services:
4
+ app:
5
+ volumes:
6
+ - .:/app
7
+ - ../:/localgems
8
+ - ssh-data:/ssh:ro
9
+ - bundler-data:/bundle
10
+
11
+ volumes:
12
+ bundler-data:
13
+ external:
14
+ name: bundler_data
15
+
16
+ ssh-data:
17
+ external:
18
+ name: ssh_data
@@ -0,0 +1,7 @@
1
+ version: '2'
2
+
3
+ services:
4
+ app:
5
+ volumes:
6
+ - .:/app
7
+ - /bundle:/bundle
@@ -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